3. 线程控制
1). 线程属性
目标:能够设置 线程的 detached/join 状态。线程栈的大小和最低地址等属性。
detached/join 状态的差别:
当线程处于 分离状态(detached)时,线程结束时。os马上回收资源。
主线程不能够调用pthread_join获取线程退出时的返回值。
当线程处于 未分离状态(join)时,线程结束时。主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源。
a)数据类型 pthread_attr_t
b)初始化及释放属性结构 pthread_attr_init pthread_attr_destroy
初始化时 会分配内存。所以一定要与 _..destroy 函数相相应
c)获取或设置线程分离状态 pthread_attr_setdetachstate pthread_attr_getdetachstate
有2种可选的状态值:
PTHREAD_CREATE_DETACHED 分离状态
PTHREAD_CREATE_JOINABLE 正常状态,能够使用pthread_join来获取状态
d)应该获取pthread_atr_destroy的返回值,由于使用pthread_attr_init初始化的时候可能分配有内存,假设释放内存失败的话,
会造成内存泄漏
e)控制线程栈的空间的大小
需求:多个线程的栈空间累计超过了进程的可用虚拟地址空间
线程调用函数的自己主动变量非常多,或者递归非常深
1)管理stackaddr线程属性。管理stacksize线程属性
pthread_attr_getstack
pthread_attr_setstack
2)获取或设置线程栈的大小
pthread_attr_setstacksize 系统帮助分配内存,自己不用管
pthread_attr_getstacksize
3)线程栈的保护
默认大小为宏PAGESIZE,但改动了栈属性后。这个值就会变成0
pthread_attr_getguardsize
pthread_attr_setguardsize
f)线程属性-并发度
pthread_attr_setconcurency
pthread_attr_getconcurency
2). 同步属性 (线程的同步对象 (例:相互排斥量、读写锁、条件变量)的属性)
实现同步的3种方式中的对象的属性
a)相互排斥量属性 pthread_mutexattr_t
1)初始化及释放 pthread_mutexattr_init pthread_mutexattr_destroy
2)进程共享属性
获取与设置共享属性 pthread_mutexattr_getpshared pthread_mutexattr_setpshared
PTHREAD_PROCESS_PRIVAE 进程内的多个线程能够訪问同一个同步对象,默认属性
PTHREAD_PROCESS_SHARED 多个进程能够共享同一块内存区域 内存共享技术。
将相互排斥量用于进程间对同一内存区訪问的同步
3)相互排斥量类型属性
目标:设置 同一线程对已上锁的相互排斥量再次上锁 是否 死锁等属性。
PTHREAD_MUTEX_NORMAL 标准的相互排斥量类型。不做错误检查或死锁检查
PTHREAD_MUTEX_DEFAULT 依赖于操作系统提供到其它类型的映射
PTHREAD_MUTEX_ERRORCHECK 提供错误检查
PTHREAD_MUTEX_RECURSIVE (递归锁)同意多次加锁,可是须要解锁相应次数 tmd,这个类型叫做递归锁
获取与设置相互排斥量类型属性
pthread_mutexattr_gettype pthread_mutexattr_settype
应用场景:
PTHREAD_MUTEX_RECURSIVE (递归锁)
当将现有的单线程接口放到多线程环境中时。使用递归锁
例:
同一线程中 func1、 func2分别对 相互排斥量加锁, 若不是用 递归锁则会出现死锁。
b)读写锁属性 pthread_rwlockattr_t
能够设置是否支持进程共享属性 (默觉得 仅仅支持 线程共享属性 )
c)条件变量属性pthread_condattr_t
能够设置是否支持进程共享属性 (默觉得 仅仅支持 线程共享属性
)
4. 线程重入
可重入函数--指在信号处理函数中。正在执行的程序被信号处理程序中断后,返回时 不能正确执行的函数。
原因:(a)使用了静态数据结构--全局的,会被其它线程/信号处理函数 等 使用
(b)调用了 malloc / free ( malloc 为它所分配的存储区维护一个链接表,运行信号处理函数时,进程可能正在改动
该表,导致被破环)
(c)标准I/O函数--大部分使用了 全局结构变量
1)线程安全:假设一个函数同一时刻能够被多个线程安全地调用
2)系统是否支持线程安全函数 sysconf(_POSIX_THREAD_SAFE_FUNCTIONS)
非线程安全的原因:返回的数据存放在静态的内存缓存区。多个线程调用该函数的时候 会覆盖前面正在使用的区域。
将其进行可重入,为其分配自己的缓存器。避免被其它线程干扰,能够保证是线程安全的。
重入后的函数,成为线程安全,并不意味着 它对信号处理程序是可重入的。
(请举例,不太懂??)
3)异步-信号安全:假设函数对异步信号处理程序的重入是安全的
4)锁文件的3个函数
flockfile ftrylockfile funlockfile
该锁是递归锁
5)确保函数在进程里面仅仅被调用一次
pthread_once_t var = PTHREAD_ONCE_INIT;
pthread_once(&var, function);
5. 线程私有数据
一种 让存储和查询 数据 与 某个线程相关的 机制。避免 与其它线程 同步訪问的问题
1)须要的数据类型: pthread_key_t
2)创建私有数据的步骤
pthread_key_t key;
a)pthread_key_create(&key, 清理函数地址) 一般通过pthread_once确保函数仅仅被运行一次。变量仅仅被初始化一次
b)char* addr = pthread_getspecific(&key)
c)为addr分配内存 malloc
d)pthread_setspecific(&key, addr);
e)pthread_key_delete删除key
f)线程退出。运行清理函数地址
6. 线程取消
设置pthread_cancle 函数相关的属性项
1)线程能够被设置为是否可取消
pthread_setcancelstate(int state, int* oldstate)
2)pthread_cancel仅仅是一个申请,仅仅有线程到达了取消点才会取消.
3)延迟取消pthread_testcancel, 适合于没有取消点的函数
4)设置取消的类型pthread_setcanceltype
7. 线程与IO
pread,pwrite 原子io操作
例:
线程A
lseek( fd, 300, SEEK_SET);
read( fd, buf1, 100);
read( fd, buf1, 100);
线程B
lseek( fd, 700, SEEK_SET);
read( fd, buf2, 100);
read( fd, buf2, 100);
当 A运行完lseek,B在A调用read之前调用lseek, 最后 两线程 读取同一条记录 (对同一文件操作,后一个 偏移量设置 覆盖了前一个设置)。
解决方法:
pread--将偏移量设定和数据读取成为一个原子操作
线程A
pread( fd, buf1, 100, 300);
线程B
pread( fd, buf2, 100, 700);
8. 线程与信号
每一个线程有自己的信号屏蔽字。可是他们共享
1)同样的信号处理函数 2)该信号与某函数的绑定,一个信号绑定到某个函数,这个被全部线程共享, 他们仅仅能看到一个
多个线程公用进程的信号屏蔽机制。除了2种情况以外:
硬件故障的信号与计时器超时的信号,仅仅递送给某个线程。其他的信号会发送给全部线程
pthread_sigmask
sigwait 等待信号发送. 一般操作须要先堵塞信号,sigwait调用会取消信号的堵塞状态,直到新信号到来
pthread_kill
sigwait(sigset_t*, int* signo)
sigwait的參数2表示捕获到的信号值
9. 线程与fork
pthread_atfork,理论内容相当多,过滤掉
当线程调用fork时。就会为子进程创建整个进程空间的副本。包含从父进程继承的相互排斥量、读写锁和条件变量等。
因此,假设父进程包括多个线程。子进程在fork返回后。没有调用exec (原来的地址空间会被丢弃)的话。就须要清理锁状态。
pthread_atfork( void (*prepare)(void), void (*parent)(void),void
(*child)(void));
能够设置3个锁清理函数。
prepare fork 由父进程 在fork创建子进程前调用。任务:获取父进程定义的全部锁。
parent fork: fork创建子进程后,但在fork返回之前,在父进程中 调用。
child fork :fork创建子进程后。但在fork返回之前。在子进程中 调用。
10. 同一进程的全部线程共享同一个计时器
11. 同一进程的全部线程共享同样的文件描写叙述符