개요
- spurious(가짜의) wakeup(일으키다)
- 조건이 충족되지 않았음에도 대기중인 스레드가 깨어나는 현상
- 운영 체제 레벨에서 조건 및 경합 처리시 규현 유연성을 위해 하나의 신호에 대해 여러 스레드를 깨우거나 신호가 없어도 깨우기도 함
- 일반적으로는 스레드가 깨어나면 조건 충족 여부를 항상 확인하여 방지
while(condition == false) {wait();}
C++
- 조건 충족 여부 확인을 직접하지 않고 대기 함수(wait, wait_for, wait_until)의 pred 인자를 통해 spurious wakeup 발생 방지 가능
- 내부적으로
while (!pred()) wait(lck);와 같이 동작
- spurious wakeup 발생 가능 코드
#include <atomic>
#include <condition_variable>
#include <cstdio>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
using namespace std;
atomic<bool> stop;
mutex mtx;
condition_variable cv;
queue<int> q;
int main() {
future<void> future1 = async(launch::async, [&]() {
while (true) {
unique_lock<mutex> lock(mtx);
cv.wait(lock);
if (stop) {
break;
}
printf("future1 wakeup\n");
q.front();
q.pop();
}
});
future<void> future2 = async(launch::async, [&]() {
while (true) {
unique_lock<mutex> lock(mtx);
cv.wait(lock);
if (stop) {
break;
}
printf("future2 wakeup\n");
q.front();
q.pop();
}
});
this_thread::sleep_for(chrono::seconds(3));
{
lock_guard<mutex> lock(mtx);
q.push(1);
}
cv.notify_one();
this_thread::sleep_for(chrono::seconds(3));
stop.store(true);
cv.notify_all();
future1.get();
future2.get();
return 0;
}
- spurious wakeup 발생 방지 코드
#include <atomic>
#include <condition_variable>
#include <cstdio>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
using namespace std;
atomic<bool> stop;
mutex mtx;
condition_variable cv;
queue<int> q;
int main() {
future<void> future1 = async(launch::async, [&]() {
while (true) {
unique_lock<mutex> lock(mtx);
while (q.empty() && stop == false) {
cv.wait(lock);
}
if (stop) {
break;
}
printf("future1 wakeup\n");
q.front();
q.pop();
}
});
future<void> future2 = async(launch::async, [&]() {
while (true) {
unique_lock<mutex> lock(mtx);
cv.wait(lock, [&]() { return q.size() || stop; });
if (stop) {
break;
}
printf("future2 wakeup\n");
q.front();
q.pop();
}
});
this_thread::sleep_for(chrono::seconds(3));
{
lock_guard<mutex> lock(mtx);
q.push(1);
}
cv.notify_one();
this_thread::sleep_for(chrono::seconds(3));
stop.store(true);
cv.notify_all();
future1.get();
future2.get();
return 0;
}