C/C++條件變量使用說明
一、使用方法
C語言中,條件變量主要配合互斥鎖,用於實現“生產者-消費者”模型,使用方法如下:
生產者:
- 獲取互斥鎖 pthread_mutex_lock
- 生產商品
- 通知消費者 pthread_cond_signal
- 釋放互斥鎖 pthread_mutex_unlock
消費者:
- 獲取互斥鎖 pthread_mutex_lock
- 判斷是否存在可消費商品,是則執行3,否則等待生產者通知,直到存在商品 pthread_cond_wait
- 消費商品
- 釋放互斥鎖 pthread_mutex_unlock
二、原理分析
1、條件變量存在的意義
在“生產者-消費者”模型中,有兩大需求:
- 保護臨界資源“商品”。
- “商品”生產完成后,及時通知消費者。
針對需求1,可以使用互斥鎖,保證臨界資源的安全;針對需求2,初始的想法是,是否可以復用需求1中的互斥鎖呢,因為當消費者去獲取鎖時,如果該鎖正被生產者占用,那么消費者線程將被掛起,隨者生產者完成本次商品的生產釋放鎖,消費者線程就立刻被喚醒,似乎是可以滿足需求的,流程變為如下所示:
生產者:
- 獲取互斥鎖 pthread_mutex_lock
- 生產商品
通知消費者 pthread_cond_signal- 釋放互斥鎖 pthread_mutex_unlock
消費者:
- 獲取互斥鎖 pthread_mutex_lock
判斷是否存在可消費商品,是則執行3,否則等待生產者通知,直到存在商品 pthread_cond_wait- 消費商品
- 釋放互斥鎖 pthread_mutex_unlock
通過簡單分析,就可以得出,該方式存在巨大問題:在生產者未進行生產時,消費者每次都將無任何阻礙的獲得鎖,發現沒有商品,又立刻釋放鎖,從而導致線程空轉,極大占用CPU處理能力。
因此,需要引入一種機制,當消費者發現無商品可處理時,將自身掛起,當有新的商品產生時,立刻喚醒處理,由此,條件變量便產生了。
2、條件變量的本質
等待條件變量(pthread_cond_wait)可依次拆分為三個操作:釋放互斥鎖、等待在條件變量上、再次獲取互斥鎖。由此,可以理解消費者檢查商品並等待的邏輯為:
while(沒有商品?)
pthread_cond_wait(mutex);
即判斷如果沒有商品,則釋放互斥鎖,等待在條件變量上;當生產者生產了商品后,消費者立刻被喚醒,又再次獲取互斥鎖,重復以上的邏輯。
三、舉例說明
為了使代碼更簡練,使用c++進行舉例,說明如何使用條件變量,實現“生產者-消費者”模型:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <string>
#include <mutex>
#include <atomic>
#include <unistd.h>
using namespace std;
mutex m;
condition_variable cv;
atomic<bool> stop(false);
unsigned int products = 0;
void consumer_thead()
{
while(!stop || products) {
unique_lock<mutex> lk(m);
cv.wait(lk, []{return products;});
cout << "consumer thread is consuming product, remain "
<< products << "\n";
products--;
}
}
void producer_thread()
{
int total = 0;
while(!stop) {
m.lock();
products += 2;
total += 2;
cout << "producer thread produced product, remain "
<< products << " total " << total << endl;
if (10 <= total) {
cout << "will stop produce" << endl;
stop = true;
}
cv.notify_one();
m.unlock();
sleep(1);
}
}
int main()
{
thread consumer(consumer_thead);
thread producer(producer_thread);
consumer.join();
producer.join();
return 0;
}