c++11_多線程_4:condition_variable

  • 引:由上一篇mutex的介紹,基本可知通過設置mutex鎖可以解決不同線程修改共享變量的線程安全問題。然而,有些時候並不止要求不同線程間訪存的數據安全,而且需要各線程按照某一順序進行訪存或執行(條件變量是併發程序設計中的一種控制結構),這種情況就需要通過設置condition variable(條件變量)實現。條件變量的一般用法是:線程 A 等待某個條件並掛起,直到線程 B 設置了這個條件,並通知條件變量,然後線程 A 被喚醒。經典的 「生產者-消費者」 問題就可以用條件變量來解決。
  • note1:條件變量的等待函數wait(lck) 需要與一個互斥鎖搭配使用 ,要明白搭配使用的機理,否則理解上會造成混亂:在調用wait時:
    • 首先該線程需要獲取到保護v的鎖 進入臨界取,因爲 v 應該是多個線程可以訪問的。
    • 在wait函數執行如下操作:
      • wait函數內部首先 mu.unlock() 釋放鎖 ;
      • 然後進入等待;
      • 如果被喚醒,則調用mu.lock() 再次獲取鎖。
  • note2:condition_variable類更多成員函數含義和用法可見.
  • 實例
#include<iostream>
#include<string>
#include<thread>
#include<mutex>
#include<condition_variable>

std::mutex mutex;
std::condition_variable cv;
std::string data;
bool ready = false;//條件
bool processed = false;//條件

void Worker()
{
	std::unique_lock<std::mutex> lock(mutex);
	//等待主線程發送數據
	cv.wait(lock, []() {return ready; });

	// 等待後,繼續擁有鎖。
	std::cout << "工作線程正在處理數據" << std::endl;

	// 睡眠一秒以模擬數據處理。
	std::this_thread::sleep_for(std::chrono::seconds(1));
	data += "已處理";

	processed = true;
	std::cout << "工作線程通知數據已經處理完畢" << std::endl;

	// 通知前,手動解鎖以防正在等待的線程被喚醒後又立即被阻塞。
	lock.unlock();

	cv.notify_one();
}

int main()
{
	std::thread worker(Worker);

	{
		std::lock_guard<std::mutex> lock(mutex);
		std::cout << "主線程正在準備數據..." << std::endl;

		std::this_thread::sleep_for(std::chrono::seconds(1));
		data = "樣本數據";
		ready = true;
		std::cout << "主線程通知數據已經準備完畢" << std::endl;
	}
	cv.notify_one();

	{
		std::unique_lock<std::mutex> lock(mutex);
		cv.wait(lock, [] {return processed; });
	}
	std::cout << "回到主線程, 數據 = " << data << std::endl;
	worker.join();

	system("pause");
	return 0;
}
  • 代碼說明:
    • 與條件變量搭配使用的「鎖」,必須是 unique_lock,不能用 lock_guard(unique_lock鎖機制更加靈活,可以再需要的時候進行lock或者unlock調用,而lock_guard不行),通過上邊wait函數的原理即可知爲什麼只能是unique_lock而不能是lock_guard。
    • 條件變量被通知後,掛起的線程就被喚醒,但是喚醒也有可能是假喚醒,或者是因爲超時等異常情況,所以被喚醒的線程仍要檢查條件是否滿足,所以 wait 是放在條件循環裏面。cv.wait(lock, [] { return ready; }); 相當於:while (!ready) { cv.wait(lock); }。(condition_variable各成員函數詳細
    • 請注意理解main函數中兩個大括號{}使用的意義(變量作用域與對象析構)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章