Linux线程唤醒与等待

生产者消费者模式在程序设计中出现频率非常高,经常会有线程间通过消息队列或其他共享变量进行交互的场景。而这时就会出现一个问题,消费者如何知道生产者已经生产了数据呢?有的程序会采取消费者循环判断消息队列大小是否为0,如果不为0则取出数据之类的方法。但是该种方法带来两个问题:

1. 生产者产出数据到消费者获得数据的延时较大。

2. CPU占用较高。

如果需要降低延时,则必然要提高轮询的频率,那么CPU占用就会升高。反之亦然,两者无法同时解决。

于是,唤醒等待机制就成为适合该种场景的解决方案。

该机制需要一个互斥对象以及条件变量共同完成,如下:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex;

其中条件变量使用宏结构常量进行赋值。接下来进行互斥对象与条件变量的初始化:

pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);

生产者唤醒逻辑:

pthread_mutex_lock(&mutex);  
pthread_cond_signal(&cond);   
pthread_mutex_unlock(&mutex);

消费者等待逻辑:

pthread_mutex_lock(&mutex);  
pthread_cond_wait(&cond, &mutex);  
pthread_mutex_unlock(&mutex);

看到这里可能会有点疑问,为何除了条件变量还需要一个互斥对象呢?等待时为什么需要条件变量和互斥对象共同生效呢?

条件变量的操作也需要达到线程安全的要求,因此需要互斥对象来进行保证。避免两个线程同时操作条件变量引发问题。而通过查阅pthread_cond_wait()的相关资料可知,当程序运行到pthread_cond_wait()时,会将互斥对象锁释放,以便生产者能够顺利唤醒。而在消费者被成功唤醒,pthread_cond_wait()等待完成后,互斥对象会被重新上锁直到手动释放。

下方提供完整的DEMO以供参考:

/* test.cpp */

#include <pthread.h>
#include <iostream>
#include <signal.h>
#include <stdlib.h>  
#include <unistd.h>  

using namespace std;

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex;

void *ThreadFunc(void *arg) {  
  
  pthread_mutex_lock(&mutex);  
  /*此处等待唤醒*/
  cout << "Thread sleeping..." << endl;
  pthread_cond_wait(&cond, &mutex);  
  /*唤醒成功*/    
  cout << "Thread awakened!" << endl;
  pthread_mutex_unlock(&mutex);

  return NULL;
}

int main(void) {

  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond, NULL);

  pthread_t tid;
  pthread_create(&tid, NULL, ThreadFunc, NULL);  

  /*等待5秒再唤醒,方便观察*/
  usleep(5000000);

  pthread_mutex_lock(&mutex);  
  /*唤醒*/
  pthread_cond_signal(&cond);   
  pthread_mutex_unlock(&mutex);

  return 0;
}

附上操作命令:

g++ test.cpp -o test -pthread
./test

posted on 2019-09-25 16:10  DluT_eDdy  阅读(2512)  评论(0编辑  收藏  举报

导航