[methodology] spurious wakeup
Updated:
개요
- 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; }