第十二章-线程控制

一、线程属性

二、同步属性

三、重入

四、线程特定数据

线程特定数据(thread-specific data),也称为线程私有数据(thread-private data),是存储和查询某个特定线程相关数据的一种机制。

在分配线程特定数据之前,需要创建与该数据关联的键。这个键用于获取对线程特定数据的访问:

#include <pthread.h>
int pthread_key_create(pthread_key_t* keyp, void (*destructor)(void*));

创建的键存储在keyp指向的内存单元中,这个键可以被进程中所有线程使用,但每个线程把这个键与不同的线程特定数据地址进行关联。

函数还可为该键关联一个可选择的析构函数,当这个线程退出时,如果数据地址已经被置为非空值,那么析构函数就会被调用,它唯一的参数就是该数据地址;如果地址为空,则表明无析构函数。

线程可以为线程特定数据分配多个键,每个键都可以有一个析构函数与它关联。

对所有线程,可以通过调用pthread_key_delete来取消键与线程特定数据值之间的关联关系:

#include <pthread.h>
int pthread_key_delete(pthread_key_t key);

调用pthread_key_delete并不会激活与键关联的析构函数,所以需要手动释放。

为了确保分配的键不会由于在初始化阶段的竞争而产生变动,使用pthread_once:

#include <pthread.h>
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t* initflag, void(*initfn)(void));

init必须是全局变量或静态变量,而且必须初始化为PTHREAD_ONCE_INIT。若每个线程都调用pthread_once,系统就能保证initfn只会被调用一次。在initfn中进行键的创建动作即可。

键创建后,可以把键和线程特定数据关联起来,可以获取特定数据地址

#include <pthread.h>
void* pthread_getspecific(pthread_key_t key);
//返回线程特定数据地址;若没有值与键关联,则返回NULL
int pthread_setspecific(pthread_key_t key, const void* value);
//成功,返回0;出错,返回错误编号

五、取消选项

六、线程与信号

每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。

进程中的信号时递送到单个线程的。若信号与硬件故障相关,则该信号一般会发送到引起该事件的线程中去,其他信号则被发送到任意一个线程。

进程可以用sigpromask来阻止信号发送,线程中必须使用:

#include <signal.h>
int pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
//成功返回0,出错返回错误码

how参数可取SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK。参数意义分别是向信号屏蔽字添加、移除、替换信号集。

线程等待一个或多个信号的出现:

#include <signal.h>
int sigwait(const sigset_t* set, int* signop)

set指定要等待的信号集,返回时,signop指向发送信号的数量。

要把信号发送给进程,可以调用kill。要把信号发送给线程:

int pthread_kill(pthread_t thread, int signo);

可以通过传一个0值的signo来检查线程是否存在。

注:闹钟定时器是进程资源,并且所有线程共享相同的闹钟。

七、线程和fork

子进程通过继承整个地址空间的副本,继承了每个互斥量、读写锁和条件变量的状态。在子进程内部,只存在一个线程,它是由父进程调用fork的线程的副本构成的。如果父进程的线程中占有锁,子进程同样占有这些锁。问题是子进程并不包含占有锁的线程的副本,所以子进程没有办法知道它占有了那些锁、需要释放哪些锁。

若子进程从fork返回后立马调用其中一个exec函数,旧的地址空间就被丢弃,所以锁的状态就无关紧要。但如果子进程需要继续处理工作的话,还需要采取其他策略:
清除锁状态

int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
  • prepare:父进程在fork创建子进程之前调用。目的是获取父进程定义的所有锁
  • parent:fork创建子进程之后、返回之前,在父进程上下文中调用。目的是对prepare获取的所有锁进行解锁
  • child:fork返回之前,在子进程的上下文中调用。目的也是释放prepare获取的所有锁

八、线程和I/O

pread(原子操作) 和 pwrite解决对文件描述符并发读写问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值