1 분 소요

개요

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