std::condition_variable notify_one()與notify_all()的區別

notify_one()notify_all()常用來喚醒阻塞的線程,線程被喚醒後立即嘗試獲得鎖。

notify_one()因爲只喚醒一個線程,不存在鎖爭用,所以能夠立即獲得鎖。其餘的線程不會被喚醒,需要等待再次調用notify_one()或者notify_all()

notify_all()會喚醒所有阻塞的線程,存在鎖爭用,只有一個線程能夠獲得鎖。那其餘未獲取鎖的線程接着會怎麼樣?會阻塞?還是繼續嘗試獲得鎖?答案是會繼續嘗試獲得鎖(類似於輪詢),而不會再次阻塞。當持有鎖的線程釋放鎖時,這些線程中的一個會獲得鎖。而其餘的會接着嘗試獲得鎖。

看下面的例子:

// condition_variable example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

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

void print_id(int id) {
	std::unique_lock<std::mutex> lck(mtx);
	while (!ready) cv.wait(lck);
	// ...
	std::cout << "thread " << id << '\n';
}

void go() {
	std::unique_lock<std::mutex> lck(mtx);
	ready = true;
	cv.notify_all(); // 這是重點
}

int main()
{
	std::thread threads[10];
	// spawn 10 threads:
	for (int i = 0; i < 10; ++i)
		threads[i] = std::thread(print_id, i);

	std::cout << "10 threads ready to race...\n";
	go();                       // go!

	for (auto& th : threads) th.join();

	return 0;
}

運行結果爲:

10 threads ready to race...
thread 2
thread 0
thread 9
thread 4
thread 6
thread 8
thread 7
thread 5
thread 3
thread 1

輸出表明所有線程被喚醒,然後依舊獲得了鎖。

如果將go()中的cv.notify_all()改爲cv.notify_one(),運行結果爲:

10 threads ready to race...
thread 0

輸出表明只有有一個線程被喚醒,然後該線程釋放鎖,這時鎖已經處理非鎖定狀態,但是其餘線程依舊處於阻塞狀態。

所以,線程阻塞在condition_variable時,它是等待notify_one()或者notify_all()來喚醒,而不是等待鎖可以被鎖定來喚醒。
線程被喚醒後,會通過輪詢方式獲得鎖,獲得鎖前也一直處理運行狀態,不會被再次阻塞。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章