三 Linux--多线程( 二 )

放数据和取数据

  • 生产者进行相关操作前先上锁,队列如果为满就需要挂起等待 。队列不为满就生成一个数据 , 然后需要把数据放入阻塞队列中,解开锁之后唤醒消费者,喊消费者进行消费 。
  • 消费者进行相关操作前先上锁,队列如果为空就需要挂起等待 。队列不为空就需要从阻塞队列中取一个数据,然后解开锁之后唤醒消费者,喊生产者进行生产 。
void ProductData(T data){LockQueue();while (IsFull()){// 让productor挂起等待ProductorWait();}// 放数据_q.push(data);UnLockQueue();// 唤醒consumerWakeUpConsumer();}void ConsumeData(T& data){LockQueue();while (IsEmpty()){// 让consumer挂起等待ConsumerWait();}// 取数据data = https://www.huyubaike.com/biancheng/_q.front();_q.pop();UnLockQueue();// 唤醒productorWakeUpProductor();}注意: 在临界资源判断唤醒条件是否就绪应该使用while循环检测 , 被唤醒的线程并不着急立即往下执行,而是再进行一次检测,判断当前唤醒条件是否真的就绪了 。因为唤醒线程的这个函数调用可能会发生失败,且线程可能是在条件不满足的时候被唤醒,发生误判被伪唤醒 。
封装一个任务我们可以实现一个任务类,生产者把这个任务放进阻塞队列中,消费者取出并进行处理 。其中还有一个run的任务执行方法
class Task{public:Task(int a = 0, int b = 0):_a(a),_b(b){}int Run(){return _a + _b;}int GetA(){return _a;}int GetB(){return _b;}private:int _a;int _b;};单生产者和单消费者模型分析BlockQueue<Task>* q;// 阻塞队列void* Consumer(void* arg){long id = (long)arg;while (1){// 消费(?。┦?Task t(0, 0);q->ConsumeData(t);cout << "consumer " << id << " consumes a task: " << t.GetA() << " + " << t.GetB() << " = " << t.Run() << endl;sleep(1);// 后面可注释,调整速度}}void* Productor(void* arg){long id = (long)arg;while (1){// 生产(放)数据int x = rand()%10 + 1;int y = rand()%10 + 1;Task t(x, y);cout << "productor " << id << " produncs a task: " << x << " + " << y << " = ?" << endl;q->ProductData(t);sleep(1);// 后面可注释,调整速度}}int main(){srand((size_t)time(nullptr));// 创建一个交易场所q =new BlockQueue<Task>;pthread_t p, c;pthread_create(&p, nullptr, Productor, (void*)(1));pthread_create(&c, nullptr, Consumer, (void*)(1));pthread_join(p, nullptr);pthread_join(c, nullptr);delete q;return 0;}代码运行的结果分三种情况分析:
1.消费者和生产者以相同的速度执行
三 Linux--多线程

文章插图
可以看出的是 , 生产者生成完一个任务 , 消费者就处理了一个任务 。接着生产者继续生产,消费者跟着处理,二者步调一致 , 一直并行的状态
2.生产者快,消费者慢
三 Linux--多线程

文章插图
生产者生产速度快 , 导致一上来生产者就把队列塞满了任务,接着唤醒消费者来消费 , 然后挂起等待 。紧接着消费者处理完一个任务就唤醒生产者来生产,生产者生产了一个任务就喊消费者来消费 , 然后继续挂起 。可以看出的是,在这种情况下,队列长时间是满的,生产者大部分时间是挂起等待的 。生产者和消费者开始一小部分时间内步调是不一致的,生产者生产完,消费者才消费是串行的,但是过了一会,二者步调就变得一致了,且速度是随消费者的
3.生产者慢 , 消费者快
三 Linux--多线程

文章插图
生产者的速度慢,生产者生产一个任务立马唤醒消费者,消费者处理完一个数据 , 发现队列为空,然后挂起等待,接着生产者继续生产一个任务,然后唤醒消费者 。可以看出的是 , 队列大部分时间是为空的 , 消费者大部分时间是处于挂起等待的,二者步调一直是一致的,且执行速度是随生产者的,也是并行的
多生产者和多消费者模型分析做到几点:
  • 生产者之间需要互斥,也就是生产者和生产者之间需要组内竞争一把锁,消费者也是如此
  • 生产者和消费者之间用互斥量和条件变量做到同步和互斥(上面就做到了)
#define P_COUNT 3#define C_COUNT 3BlockQueue<Task>* q;pthread_mutex_t c_lock;// 消费者的锁pthread_mutex_t p_lock;// 生产者的锁void* Consumer(void* arg){long id = (long)arg;while (1){pthread_mutex_lock(&c_lock);// 消费(?。┦?Task t(0, 0);q->ConsumeData(t);cout << "consumer " << id << " consumes a task: " << t.GetA() << " + " << t.GetB() << " = " << t.Run() << endl;pthread_mutex_unlock(&c_lock);sleep(1);}}void* Productor(void* arg){long id = (long)arg;while (1){pthread_mutex_lock(&p_lock);// 生产(放)数据int x = rand()%10 + 1;int y = rand()%10 + 1;Task t(x, y);cout << "productor " << id << " produncs a task: " << x << " + " << y << " = ?" << endl;q->ProductData(t);pthread_mutex_unlock(&p_lock);sleep(1);}}int main(){srand((size_t)time(nullptr));pthread_mutex_init(&c_lock, nullptr);pthread_mutex_init(&p_lock, nullptr);// 创建一个交易场所q =new BlockQueue<Task>;pthread_t p[P_COUNT];pthread_t c[C_COUNT];for (long i = 0; i < P_COUNT; ++i){pthread_create(p+i, nullptr, Productor, (void*)(i+1));}for (long i = 0; i < C_COUNT; ++i){pthread_create(c+i, nullptr, Consumer, (void*)(i+1));}for (int i = 0; i < C_COUNT; ++i){pthread_join(c[i], nullptr);}for (int i = 0; i < P_COUNT; ++i){pthread_join(p[i], nullptr);}pthread_mutex_destroy(&c_lock);pthread_mutex_destroy(&p_lock);delete q;return 0;}

推荐阅读