虚假唤醒
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); }
|