UNIX环境高级编程(第12章 线程控制)
来源:互联网 发布:iphone移动数据设置 编辑:程序博客网 时间:2024/06/05 09:08
1线程属性
创建线程pthread_create的参数pthread_attr_tattr;可以用来定制各种不同的线程属性。
detachstate:线程的分离状态属性
guardsize:线程栈末尾的警戒缓冲区大小
stackaddr:线程栈的最低地址
stacksize:线程栈的大小
1.1线程属性的初始化和销毁
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr); /*将attr初始化为系统支持的默认属性值*/
int pthread_attr_destroy(pthread_attr_t *arrt);/*释放空间,无效值初始化attr*/
返回值:若成功返回0,否则返回错误编号
1.2线程的分离状态属性
#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
返回值:若成功返回0,否则返回错误编号
如果创建线程时就知道不需要了解线程的终止状态,可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。
分离状态属性detachstate有两个合法值:PTHREAD_CREATE_DETACHED,以分离状态启动线程;PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序可以获取线程的终止状态。
1.3线程的缓冲区大小
#include <pthread.h>
int pthread_attr_getguardsize (const pthread_attr_t *restrict attr, size_t *restrict guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
返回值:若成功返回0,否则返回错误编号
线程缓冲区大小属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。该属性默认值为PAGESIZE个字节。也可以把guardsize线程属性设为0,从而不允许属性的这种行为发生:在这种情况下不会提供警戒缓冲区。
1.4线程栈的最低地址
#include <pthread.h>
int pthread_attr_getstack (const pthread_attr_t *restrict attr,
void **restrict stackaddr, size_t *restrict stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
返回值:若成功返回0,否则返回错误编号
这两个函数可以用于管理stackaddr线程属性,也可以用于管理stacksize线程属性。
1.5线程栈的大小
#include <pthread.h>
int pthread_attr_getstacksize (const pthread_attr_t *restrict attr, size_t *restrict stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
返回值:若成功返回0,否则返回错误编号
这两个函数用来读取或设置线程属性stacksize。
2同步属性
2.1互斥量属性
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr); /*初始化互斥量属性*/
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); /*回收互斥量属性*/
返回值:若成功则返回0,否则返回错误编号
2.2读写锁属性
#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); /*初始化读写锁属性*/
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); /*回收读写锁属性*/
返回值:若成功则返回0,否则返回错误编号
2.3条件变量属性
#include <pthread.h>
int pthread_condattr_init(pthread_condattr_t *attr); /*初始化条件变量属性*/
int pthread_condattr_destroy(pthread_condattr_t *attr); /*回收条件变量属性*/
返回值:若成功则返回0,否则返回错误编号
3重入
可重入与不可重入:(转载)
这种情况出现在多任务系统当中,在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的。
众所周知,在进程中断期间,系统会保存和恢复进程的上下文,然而恢复的上下文仅限于返回地址,cpu寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。打个比方,比如malloc,将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
满足下面条件之一的多数是不可重入函数:
(1)使用了静态数据结构;
(2)调用了malloc或free;
(3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
(4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。
如果一个函数对多个线程来说是可重入的,则说这个函数是线程安全的,但这并不能说明对信号处理程序来说该函数也是可重入的。
如果函数对异步信号处理程序的重入是安全的,那么就可以说函数是异步-信号安全的。
(转载)
可重入函数,与多线程无关,即可重入概念并不依赖于多线程,可重入的提出是依据单一线程提出来的,当然,多线程可重入是它的扩展。一个函数被同一个线程调用2次以上,得到的结果具有可再现性(多次调用函数,得到的结果是一样的)。那么我们说这个函数是可重入的。
为了保证函数是可重入的,需要做到一下几点:
1,不在函数内部使用静态或者全局数据
2,不返回静态或者全局数据,所有的数据都由函数调用者提供
3,使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4,如果必须访问全局数据,使用互斥锁来保护
5,不调用不可重入函数
线程安全函数,如果一个函数能够安全的同时被多个线程调用而得到正确的结果,那么,我们说这个函数是线程安全的。所谓安全,一切可能导致结果不正确的因素都是不安全的调用。
线程安全,是针对多线程而言的。那么和可重入联系起来,我们可以断定,可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。不可重入函数,函数调用结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调用,那么,这个不可重入函数就是转换成了线程安全。
4线程私有数据
线程私有数据(也称线程特定数据)是存储和查询与某个线程相关的数据的一种机制。
1在分配线程私有数据之前,需要创建与该数据关联的键:
#include <pthread.h>
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void));
返回值:成功返回0,失败返回错误编号
2消除键与线程私有数据值之间的关联关系
#include <pthread.h>
int pthread_key_delete(pthread_key_t *key);
返回值:成功返回0,失败返回错误编号
3确保分配的键并不会由于在初始化阶段的竞争而发生变动
#include <pthread.h>
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void (*initfn)(void));
返回值:成功返回0,失败返回错误编号
5取消选项
有两个线程属性并没有包含在pthread_attr_t结构中,它们是可取消状态和可取消类型。这两个属性影响着线程在响应pthread_cancel函数调用时所呈现的行为。
6线程和信号
1线程中阻止信号发生
#include <pthread.h>
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
返回值:成功返回0,失败返回错误编号
2线程等待一个或多个信号发生
#include <pthread.h>
int sigwait(const sigset_t *restrict set, int *restrict signop);
返回值:成功返回0,失败返回错误编号
3把信号发送到线程
#include <pthread.h>
int pthread_kill(pthread_t thread, int signo);
返回值:成功返回0,失败返回错误编号
#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <pthread.h>int quitflag;sigset_t mask;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t wait = PTHREAD_COND_INITIALIZER;void *thr_fn(void *arg){ int err, signo; for (;;) { err = sigwait(&mask, &signo); if (err != 0) { printf("sigwait failed..\n"); exit(0); } switch(signo) { case SIGINT: printf("\ninterrupt\n"); break; case SIGQUIT: pthread_mutex_lock(&lock); printf("\nquit\n"); quitflag = 1; pthread_mutex_unlock(&lock); pthread_cond_signal(&wait); return 0; default: printf("unexpeced signal %d\n", signo); exit(1); } }}int main(void){ int err; sigset_t oldmask; pthread_t tid; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); if ((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0) { printf("SIG_BLOCK error\n"); exit(1); } err = pthread_create(&tid, NULL, thr_fn, 0); if (err != 0) { printf("can't create thread\n"); exit(1); } pthread_mutex_lock(&lock); while (quitflag == 0) pthread_cond_wait(&wait, &lock); pthread_mutex_unlock(&lock); quitflag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) { printf("SIG_SETMASK error\n"); exit(1); } return 0;}
[root]# ./a.out interruptinterruptinterruptquit[root]#
- UNIX环境高级编程(第12章 线程控制)
- 《UNIX环境高级编程》 第12章 线程控制 【读书笔记】
- UNIX环境高级编程-第12章- 线程控制 - 一
- UNIX环境高级编程-第12章- 线程控制 - 二
- 《UNIX环境高级编程》第12章 线程控制
- UNIX环境高级编程(第8章 进程控制)
- UNIX环境高级编程(第11章 线程)
- unix高级环境编程(第12章高级终端)
- UNIX环境高级编程第十二章 线程控制 总结
- UNIX环境高级编程(十二)线程控制
- unix环境高级编程-线程控制(1)
- unix环境高级编程-线程控制(2)
- UNIX环境高级编程-第8章- 进程控制 - 一
- UNIX环境高级编程-第8章- 进程控制 - 二
- UNIX环境高级编程之第8章:进程控制
- 《UNIX环境高级编程》第8章 进程控制
- 《UNIX环境高级编程》第11章线程【读书笔记】
- UNIX环境高级编程-第11章- 线程 - 一
- Java线程(一):线程安全与不安全
- 将页面上的javascript错误获取到并打印到页面上的方法
- Wi-Fi Direct(Wi-Fi P2P)
- LeetCode 48 Anagrams
- ByteBuffer用法小结(对刚接触NIO的童鞋有用)
- UNIX环境高级编程(第12章 线程控制)
- Android_Intent意图详解
- 进度条
- 如何复制一个java对象(浅克隆与深度克隆)
- 模型选择之特征选择
- mysql 权限管理及client 连接整理
- java 和 oc 比较
- nginx实现带参数目录域名重定向二级域名方法
- JS 双竖线运算符||返回有效值