各类资料学习下载合集
链接:https://pan.quark.cn/s/b0a2f36933de
在多线程编程中,我们经常会遇到需要线程之间协同工作的情况。例如,一个线程负责生产数据,另一个线程负责消费数据。如果生产者生产过快,消费者来不及处理;或者消费者消费过快,生产者还没来得及生产,都会导致问题。
这时,生产者-消费者模型就成为了解决此类并发协作问题的经典范式。
一、 生产者-消费者模型:解决并发协作的经典范式
1. 模型基本结构
生产者-消费者模型由以下三部分组成:
- 生产者线程 (Producer):负责生成数据,并将其放入一个公共区。
- 消费者线程 (Consumer):负责从公共区取出数据,并进行处理。
- 公共区 (Shared Buffer/Queue):一个共享的数据存储区域,连接生产者和消费者。
2. 典型示例
- 餐厅场景:
- 厨师 (生产者):烙饼,然后将烙好的饼放入一个筐中 (公共区)。
- 食客 (消费者):从筐中取出饼来吃。
- 电商库存管理:
- 商家 (生产者):向仓库 (公共区)补货。
- 买家 (消费者):从仓库购买商品。
这个模型具有良好的扩展性,可以支持多个生产者和多个消费者同时操作同一个公共区。
3. 模型中的挑战
核心挑战在于如何实现生产者和消费者之间的同步与互斥:
- 互斥:生产者和消费者在访问公共区时,必须互斥,防止数据混乱。
- 同步:
- 当公共区为空时,消费者必须等待生产者生产数据。
- 当公共区为满时,生产者必须等待消费者消费数据。
二、 条件变量:模型中的“等待-通知”机制
条件变量(Condition Variable)是解决生产者-消费者模型中“同步”问题的关键。它本身不是锁,但能让线程阻塞等待某个条件成立,并与互斥锁配合使用。
1.pthread_cond_wait函数的“三重作用”
这是条件变量实现同步的核心函数,它具备原子性的“三重作用”:
- 阻塞等待:将调用线程置于条件变量的等待队列中,使其进入阻塞状态。
- 原子解锁:在线程阻塞的同时,原子性地释放它之前持有的互斥锁。这个原子操作至关重要,它确保了线程在进入等待状态之前不会错过任何信号。
- 重新加锁:当条件满足线程被唤醒时,它会重新获取该互斥锁,只有成功获取后函数才会返回。
使用前提:调用pthread_cond_wait()之前,线程必须已经持有其配套的互斥锁。
2.signal与broadcast:唤醒策略
pthread_cond_signal():- 设计意图:唤醒一个阻塞在条件变量上的线程。
- 实际实现:虽然标准规定唤醒一个,但某些实现中可能唤醒多个线程(即“虚假唤醒”)。因此,在
wait之后,必须使用while循环而非if语句来重新检查条件。
pthread_cond_broadcast():- 设计意图:唤醒所有阻塞在条件变量上的线程。
- 考虑:可能导致“惊群效应”(Thundering Herd),即大量线程被唤醒后争抢资源,造成不必要的上下文切换。根据具体场景选择使用。
三、 代码实战:多生产者多消费者模型
我们将实现一个经典的多生产者、多消费者、有界缓冲区模型。
1. 代码示例 (prod_cons_cond.c)
#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<unistd.h>#include<time.h>// For time()#defineBUFFER_SIZE5// 缓冲区大小#defineNUM_PRODUCERS2// 生产者数量#defineNUM_CONSUMERS3// 消费者数量