Updated:

1 minute read

개요

  • 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;
     }