生產者消費者模式在程序設計中出現頻率非常高,經常會有線程間通過消息隊列或其他共享變量進行交互的場景。而這時就會出現一個問題,消費者如何知道生產者已經生產了數據呢?有的程序會采取消費者循環判斷消息隊列大小是否為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