天天看點

多線程(c++11)------條件變量

什麼是條件變量?

條件變量是一種同步機制,允許線程挂起,直到共享資料上的某些條件得到滿足。條件變量上的基本操作有:觸發條件(當條件變為 true 時);等待條件,挂起線程直到其他線程觸發條件。條件變量要和互斥量相聯結,以避免出現條件競争--一個線程預備等待一個條件變量,當它在真正進入等待之前,另一個線程恰好觸發了該條件。

什麼意思呢?不清楚沒關系。看了例子就知道了:問題描述:假設有一個bool型全局變量 isTrue ,現有10個線程,線程流程如下:當isTrue為真時,doSomething;否則挂起線程,直到條件滿足。那麼,用thread和mutex如何實作這個功能呢?

#include <vector>
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace  std;

bool isTrue = false;

void doSomething()
{
	cout << "this is : " << this_thread::get_id() << endl;
}

void thread_Func()
{
	while (!isTrue)
		this_thread::yield();

	doSomething();
}

int main()
{
	vector<thread> v;
	v.reserve(10);
	
	for (int i = 0; i < 10; i++)
	{
		v.emplace_back(thread_Func);
	}

	this_thread::sleep_for(chrono::seconds(2));

	isTrue = true;

	for (auto& t : v)
	{
		t.join();
	}
	return 1;
}
           

這段代碼雖然能滿足需求,但有一個大缺點,就是當條件為假時,子線程會不停的測試條件,這樣會消耗系統資源。我們的思想是,當條件為假時,子線程挂起,直到條件為真時,才喚醒子線程。

nice,條件變量就是幹這事的!

先來看看條件變量的介紹:

條件變量能夠挂起調用線程,直到接到通知才會喚醒線程。它使用unique_lock<Mutex>來配合完成。下面是用條件變量實作需求:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>

using namespace  std;

bool isTrue = false;
std::mutex mtx;
std::condition_variable cv;

void doSomething()
{
	cout << "this is : " << this_thread::get_id() << endl;
}

void thread_Func()
{
	unique_lock<mutex> loc(mtx);
	while (!isTrue)
		cv.wait(loc);

	doSomething();
}



int main()
{
	vector<thread> v;
	v.reserve(10);

	for (int i = 0; i < 10; i++)
	{
		v.emplace_back(thread_Func);
	}

	this_thread::sleep_for(chrono::seconds(2));

	{
		unique_lock<mutex> loc(mtx);
		isTrue = true;
		cv.notify_all();
	}

	for (auto& t : v)
	{
		t.join();
	}
	return 1;
}
           

我們發現,在條件變量cv的wait函數中,我們傳入了一個lock參數,為什麼要用鎖呢?因為isTrue為臨界變量,主線程中會“寫”它,子線程中要“讀”它,這樣就産生了資料競争,并且這個競争很危險。

我們把while (!isTrue) cv.wait(loc)拆開:

1、條件判斷

2、挂起線程

假如現在1執行完後,時間片輪轉,該子線程暫停執行。而恰好在這時,主線程修改了條件,并調用了cv.notify_all()函數。這種情況下,該子線程是收不到通知的,因為它還沒挂起。等下一次排程子線程時,子線程接着執行2将自己挂起。但現在主線程中的notify早已經調用過了,不會再調第二次了,是以該子線程永遠也無法喚醒了。

為了解決上面的情況,就要使用某種同步手段來給線程加鎖。而c++11的condition_variable選擇了用unique_lock<Mutex>來配合完成這個功能。并且我們隻需要加鎖,條件變量在挂起線程時,會調用原子操作來解鎖。

c++11還為我們提供了一個更友善的接口,連同條件判斷,一起放在條件變量裡。上面的線程函數可做如下修改:

void thread_Func()
	{
	unique_lock<mutex> loc(mtx);
	cv.wait(loc, []() -> bool { return isTrue;} );

	doSomething();
	}
           

繼續閱讀