前言作者数据结构还剩下AVL树红黑树等结构未复习准备同时推进操作系统和网络的代码先从阻塞队列入手这是一个很经典的生产与消费模型1. 阻塞队列的数据量上限const static int gcap 10; ... private: std::queueT _q; int _cap;阻塞队列类中会有一个成员变量_cap代表阻塞队列最多数据的上限决定了生产者最多能往阻塞队列中生产多少数据2. 条件变量在阻塞队列中的作用pthread_cond_t _producer_cond; pthread_cond_t _consumer_cond;条件变量可以同步生产和消费者比如队列为空时消费者进入等待状态然后生产者生产以后唤醒消费者让消费者能够及时处理数据这就是同步3. 记录等待中的生产者或者消费者的数量int _pwait_num; int _cwait_num;比如先分析_pwait_num的作用只有当有等待的生产者时(_pwait_num 0)消费者才会进行唤醒生产者若没有等待的生产者(_pwait_num 0)消费者则不会执行pthread_cond_single代码节省了计算资源4. queue的push是拷贝所以Equeue的参数可以有const Tvoid Equeue(const T in) { pthread_mutex_lock(_mutex); ... _q.push(in);_q.push(in)只是把in的值赋值给push函数里面的形式参数但是不修改in变量的值所以Equeue的传参可以用const T in这样既能传入左值又能传入右值5. 单生产者和单消费者既有竞争关系又有同步关系-竞争关系生产者和消费者会竞争同一把锁-同步关系生产者进入等待后消费者消费完会唤醒生产者让生产者继续生产即同步6. 阻塞队列的出队对应着消费入队对应着生产出队就是把队列里面的数据拿出去给消费者即消费者消费掉入队就是把数据放入队列即生产者进行生产阻塞队列相当于一个缓冲区7. static_cast是一个类型转换运算符static_cast会把()里面的值安全地强转为里面的类型8. pthread_cond_wait会归还锁被唤醒后重新申请锁继续从wait处运行while (isFull()) { _pwait_num; pthread_cond_wait(_producer_cond, _mutex); _pwait_num--; }比如入队生产逻辑在阻塞队列满的时候生产者进入条件变量等待除了传递第一个条件变量参数还要传递一把锁这把锁就是这个线程在等待之前要释放的锁线程被唤醒后会重新竞争申请锁然后继续从pthread_cond_wait处运行9. pthread_create中的线程传递用了取地址而pthread_join中没有取地址只是传拷贝pthread_t p, c; pthread_create(p, nullptr, Produce, bq); pthread_create(c, nullptr, Consume, bq); pthread_join(p, nullptr); pthread_join(c, nullptr);测试代码中因为pthread_create中是要给p赋值的所以要把p的地址传进去而pthread_join是用来回收线程的而不再改变p的值所以直接传形参即可10. 生产者因为队列为满进入的等待需要用while循环消费者因为队列为空进入的等待也需要用while循环while (isFull()) { _pwait_num; pthread_cond_wait(_producer_cond, _mutex); _pwait_num--; }这是在多生产者、多消费者下会出现的问题比如有多个生产者被唤醒然后它们就要竞争同一把锁而被唤醒的生产者只有一个能竞争到锁。其余的就要继续进入一个while循环去等待总体实现#pragma once #include pthread.h #include queue namespace BlockQueueModule { const static int gcap 10; template class T class BlockQueue { private: bool isFull() { return _q.size() _cap; } bool isEmpty() { return _q.empty(); } public: BlockQueue(int cap gcap) :_cap(cap) ,_pwait_num(0) ,_cwait_num(0) { pthread_mutex_init(_mutex, nullptr); pthread_cond_init(_producer_cond, nullptr); pthread_cond_init(_consumer_cond, nullptr); } void Equeue(const T in) { pthread_mutex_lock(_mutex); while (isFull()) { _pwait_num; pthread_cond_wait(_producer_cond, _mutex); _pwait_num--; } _q.push(in); if (_cwait_num) { pthread_cond_signal(_consumer_cond); } pthread_mutex_unlock(_mutex); } void Pop(T *out) { pthread_mutex_lock(_mutex); while (isEmpty()) { _cwait_num; pthread_cond_wait(_consumer_cond, _mutex); _cwait_num--; } *out _q.front(); _q.pop(); if (_pwait_num) { pthread_cond_signal(_producer_cond); } pthread_mutex_unlock(_mutex); } ~BlockQueue() { pthread_mutex_destroy(_mutex); pthread_cond_destroy(_producer_cond); pthread_cond_destroy(_consumer_cond); } private: std::queueT _q; int _cap; pthread_mutex_t _mutex; pthread_cond_t _producer_cond; pthread_cond_t _consumer_cond; int _pwait_num; int _cwait_num; }; }测试代码#include BlockQueue.hpp #include unistd.h #include stdio.h using namespace BlockQueueModule; void* Produce(void* args) { BlockQueueint* bq static_castBlockQueueint*(args); int data 0; while (true) { sleep(2); bq-Equeue(data); printf(生产者生产数据: %d\n, data); data; } } void* Consume(void* args) { BlockQueueint* bq static_castBlockQueueint*(args); while (true) { int data; bq-Pop(data); printf(消费者消费数据: %d\n, data); } } int main() { BlockQueueint *bq new BlockQueueint(); pthread_t p, c; pthread_create(p, nullptr, Produce, bq); pthread_create(c, nullptr, Consume, bq); pthread_join(p, nullptr); pthread_join(c, nullptr); delete bq; return 0; }测试结果./main生产者生产数据: 0消费者消费数据: 0生产者生产数据: 1消费者消费数据: 1生产者生产数据: 2消费者消费数据: 2生产者生产数据: 3消费者消费数据: 3生产者生产数据: 4消费者消费数据: 4生产者生产数据: 5消费者消费数据: 5^C