虚假唤醒

虚假唤醒

https://zhuanlan.zhihu.com/p/652823880
https://www.cnblogs.com/angdh/p/18267978
https://cloud.tencent.com/developer/article/1557403

应用层引起的虚假唤起

这种虚假唤起是由不合理的代码逻辑引起的,具体看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <condition_variable>
#include <iostream>
#include <thread>
#include <queue>
#include <string>

using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex mtx;
std::queue<std::string> q;

void Producer()
{
std::this_thread::sleep_for(2000ms);
std::cout << "Ready Send notification." << std::endl;
mtx.lock();
q.push("message");
q.push("message");
mtx.unlock();
cv.notify_all();
std::this_thread::sleep_for(200ms);
mtx.lock();
q.push("message");
mtx.unlock();
cv.notify_all();
}

void Consumer()
{
std::cout << "Wait for notification." << std::endl;
std::unique_lock<std::mutex> lck(mtx);
if(q.empty())
cv.wait(lck);
std::string msg = q.front();
q.pop();
mtx.unlock();
std::cout << "Get: " << msg <<std::endl;
}

int main()
{
std::thread producer(Producer);
std::thread consumer(Consumer);
std::thread consumer2(Consumer);
std::thread consumer3(Consumer);
producer.join();
consumer.join();
consumer2.join();
consumer3.join();
return 0;
}

在这个例子中,有一个生产者线程与3个消费者线程,生产者线程第一次会生产2个message,但是notify_all在唤起所有3个消费者后,会有一个消费者获取不到message(最后获取锁的所有权的那个),这样就导致了那个线程被唤起后却无法获取元素。

1
2
3
4
5
6
7
Wait for notification.
Wait for notification.
Wait for notification.
Ready Send notification.
Get: message
Get: message
Get:

操作系统引起的虚假唤起

当一个线程在wait情况下被唤起时,唤起他的并不是对应的条件变量信号(如pthread_cond_signal或notify_one/signal_one等方法)直接导致的。换句话说,即使没有对线程进行显式的唤醒操作,线程也可能因为某些原因(如操作系统的内部行为)被唤醒,但此时唤醒的条件并不满足,导致线程执行错误或不必要的操作。

  • 操作系统并不保证在调用唤醒函数(如pthread_cond_signal)时只唤醒一个或特定的线程。这可能导致多个线程同时被唤醒,而实际上可能只需要唤醒一个
    比方说一些来自于操作系统的中断信号,或者出现了另外一个并不与当前wait线程匹配的信号,也有可能将此wait线程唤起。

解决办法,判断标记值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <condition_variable>
#include <iostream>
#include <thread>
#include <queue>
#include <string>

using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex mtx;
std::queue<std::string> q;

void Producer()
{
std::this_thread::sleep_for(2000ms);
std::cout << "Ready Send notification." << std::endl;
mtx.lock();
q.push("message");
q.push("message");
mtx.unlock();
cv.notify_all();
std::this_thread::sleep_for(200ms);
mtx.lock();
q.push("message");
mtx.unlock();
cv.notify_all();
}

void Consumer()
{
std::cout << "Wait for notification." << std::endl;
std::unique_lock<std::mutex> lck(mtx);
if(q.empty())
cv.wait(lck,[]{return !q.empty();});
std::string msg = q.front();
q.pop();
mtx.unlock();
std::cout << "Get: " << msg <<std::endl;
}

int main()
{
std::thread producer(Producer);
std::thread consumer(Consumer);
std::thread consumer2(Consumer);
std::thread consumer3(Consumer);
producer.join();
consumer.join();
consumer2.join();
consumer3.join();
return 0;
}

其中

1
cv.wait(lck,[]{return !q.empty();});

等价于

1
2
3
while(q.empty()){
cv.wait(lck);
}