Linux下pthread线程同步主要有两种方法:信号量(semaphore)和条件变量(condition_variable),在生产者消费者的实例中,通常用到的是信号量来进行同步。本文采用条件变量的通知机制,实现类似信号量的功能,完成生产者消费者示例,并在最后贴出代码。另外pthread的信号量有二值信号量和计数信号量两种,第一种信号量只有0和1两个值,用法类似条件变量,第二种就是传统的介于0-限制值的一个信号量,可以用来统计可用资源数目。本文就是用了notempty
和notfull
两个条件变量来表示资源空和资源已达上限两种状态。
程序步骤
首先,用户需要在main函数里创建consumer和producer两个线程,之后调用join方法等待两个线程退出。同时要用到两个条件变量notempty
和notfull
。在生产者线程中,生产一个资源之前先判断notfull
,确保资源数没到上限,之后再生产一个资源,即往消息队列中push一个Packet,然后向消费者发送一个notempty
信号表示已经生产了一个资源,可用资源数不为空。在消费者线程中,消费一个资源之前先判断notempty
,确保当前消息队列中有资源,之后再消费一个资源,即从消息队列中pop一个Packet,然后向生产者发送一个notfull
信号表示已经消费了一个资源,资源数没有到上限。生产者和消费者的处理函数正好是两个相反的对称的过程,可以用下图来表示:
实现代码
1 |
|
关于条件变量
基本用法
- 声明条件变量数据结构
pthread_cond_t
- 初始化条件变量
pthread_cond_init(&cond_var, NULL);
- 发送信号
pthread_cond_signal(&cond_var);
和等待信号pthread_cond_wait(¬empty, &lock);
- 声明条件变量数据结构
使用要点:
条件变量只有一种正确使用的方式,几乎不可能用错。对于 wait 端:
- 必须与 mutex 一起使用,该布尔表达式的读写需受此 mutex 保护。
- 在 mutex 已上锁的时候才能调用 wait()。
- 把判断布尔条件和 wait() 放到 while 循环中。
对于 signal/broadcast 端:
- 不一定要在 mutex 已上锁的情况下调用 signal (理论上)。
- 在 signal 之前一般要修改布尔表达式。
- 修改布尔表达式通常要用 mutex 保护(至少用作 full memory barrier)。
- 注意区分 signal 与 broadcast:“broadcast 通常用于表明状态变化,signal 通常用于表示资源可用。(broadcast should generally be used to indicate state change rather than resource availability。)”
关于mutex:
pthread_cond_wait()
中的wait的参数有一个mutex,这里的mutex和用于同步消息队列的mutex是不同的,可以简单理解为每一个共享资源都要对应一个mutex,消息队列是共享资源,因此线程对其读写要用mutex保护,保证每一个时刻只有一个线程可以对资源进行操作。而pthread_cond_wait()
参数里的mutex是用来保护while循环条件中的共享资源的。因为通常情况下,每一个wait函数上面都会紧随着一个while循环作为判断,这是为了避免异常唤醒,即一个线程对某一条件变量signal可能会唤醒多个的线程,或者不相关的线程,因此要用一个条件作为约束,在我们的程序中就是用消息队列中资源的个数packetQueue.size()
来防止异常唤醒。