이벤트 + 락 = Eventable Lock 클래스
스레드를 동기화할 때는 다음 형식을 매우 흔히 볼 수 있다.
스레드1
- 락을 건다.
 - 큐에 넣는다.
 - 락을 풀고 신호를 보낸다.
 
스레드2
- 신호를 기다린다.
 - 락을 건다.
 - 큐에서 꺼낸다.
 - 락을 푼다.
 
C++11로는 흔히 mutex, condition_variable을 lock_guard, unique_lock과 함께 사용해 처리하면 되며 보통 다음과 같다.
| 
					 1 2 3 4 5 6 7 8 9 10 11  | 
						// mutex mtx // condition_variable cv; void thread1() {     lock_guard<mutex> guard(mtx);     // 큐에 넣음     cv.notify_one(); }  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10  | 
						// mutex mtx // condition_variable cv; void thread2() {     unique_lock<mutex> guard(mtx);     cv.wait(guard);     // 큐에서 꺼냄 }  | 
					
이때 동기화에 사용하는 mutex와 condition_variable은 두 스레드에서 공유해야 한다. 워낙 흔하게 쓰는 형식인데다 여러 객체를 만들어 공유하는 것은 조금 귀찮으므로 신호를 보낼 수 있는 락 객체를 하나 만들면 편하다.
| 
					 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  | 
						#pragma once #include <condition_variable> #include <mutex> namespace surp_lib {     class EventableLock {     public:         enum class InitialLock {unlock, lock};         explicit EventableLock(InitialLock lock = InitialLock::lock);         ~EventableLock() = default;         EventableLock(EventableLock const&) = delete;         EventableLock& operator=(EventableLock const&) = delete;         void lock();         void unlock();         void set();         void wait();     private:         std::condition_variable condition_;         std::mutex mutex_;         std::unique_lock<std::mutex> guard_;     }; }  | 
					
| 
					 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  | 
						#include "eventable_lock.h" namespace surp_lib {     EventableLock::EventableLock(InitialLock lock)         : guard_{mutex_}     {         if (InitialLock::unlock == lock)             unlock();     }     void EventableLock::lock()     {         if (!guard_.owns_lock())             guard_.lock();     }     void EventableLock::unlock()     {         if (guard_.owns_lock())             guard_.unlock();     }     void EventableLock::set()     {         condition_.notify_one();     }     void EventableLock::wait()     {         condition_.wait(guard_);     } }  | 
					
| 
					 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  | 
						#include <iostream> #include <thread> #include "eventable_lock.h" using namespace std::chrono_literals; using std::cout; using std::this_thread::sleep_for; using std::thread; using surp_lib::EventableLock; void thread1(EventableLock& lock) {     cout << "Waiting in the thread\n";     lock.wait();     cout << "Processing in the thread\n";     sleep_for(3s);     lock.unlock(); } int main() {     EventableLock lockObj;     cout << "Create a thread\n";     thread t1(thread1, std::ref(lockObj));     sleep_for(2s);     cout << "Signal to the thread\n";     lockObj.set();     t1.join();     cout << "Program end\n";     return 0; }  | 
					
특별한 내용은 없지만 eventable_lock_test.cpp에서 21번 줄에 주의한다. unlock()을 하지 않으면 GCC와 달리 Visual Studio에서는 디버그 모드로 실행할 때 unlock of unowned mutex 메시지와 함께 프로그램을 바로 종료한다. 이는 Wrong message “unlock of unowned mutex”란 글에서 볼 수 있는 것처럼 호출하는 스레드에 해당 뮤텍스의 소유권이 있어야 하기 때문이다. 즉 해당 오류는 main()을 종료할 때 발생하는데 thread1()에서 wait() 이후 해당 뮤텍스 소유권을 유지하며, main()을 종료할 때는 주 스레드에 소유권이 없는 상태로 해제 시도하기 때문이다.