Thread 관련 함수들
std::thread
std::mutex
std::condition_variable
std::lock
Thread를 사용하는 목적: 백그라운드에서 함수가 돌아가게 하는 것.
물리적인 thread의 수는 제한되어있지만, OS가 적절하게 스케줄링을 해서 물리적인 thread에 프로세스를 알맞게 붙혀주는 것.
1 2 3 4 5 6 7 8 9 10 11 #include <thread> void foo1 () {};void foo2 () {};int main () { std::thread (foo1); std::thread (foo2); }
Read / Write 아래 코드를 실행하면 어떤 결과가 나올까?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <thread> void foo1 (int a) { a = 1 ; }; void foo2 (int a) { a = 2 ; }; int main () { int a = 0 ; std::thread (foo1, a); std::thread (foo2, a); a = 3 ; std::cout << a << std::endl; }
정답: a가 뭐가 나올지 알 수 없다 + 터질 수도 있다.
a의 값을 read만 하는거는 터지지는 않는다. 물론 이상한 값이 나올 수 있다. 위 코드는 a를 write하고 있다. Write 중에 접근하면 터질 수 있다.
‘터질 수도 있다’라는 이유는, Write하는 시점을 정하는거는 순전히 OS 종속적이기 때문이다.
Unique lock / Mutex 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 <atomic> #include <thread> #include <condition_variable> #include <mutex> void foo1 (int a, std::mutex& mutex_for_a) { std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; a = 1 ; lock_for_a.unlock (); }; void foo2 (int a, std::mutex& mutex_for_a) {{ std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; a = 2 ; lock_for_a.unlock (); }; int main () { std::mutex mutex_for_a; std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; int a = 0 ; lock_for_a.unlock (); std::thread thread1 (foo1, a) ; std::thread thread2 (foo2, a) ; lock_for_a.lock (); a = 3 ; std::cout << a << std::endl; }
unique_lock을 걸어서 누가 먼저 실행할지 결정할 수 있다. 위의 코드는 a는 무조건 3이 나오는 코드이다. lock을 걸고나서 수행하는 최종 연산이 a=3 대입연산이기 때문이다. 하지만 중간에 foo1이 먼저 실행될지, foo2가 먼저 실행될지는 모른다. Thread가 생성되는 시간도 고려해야한다.
Dual lock 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 39 40 41 42 43 44 45 46 47 #include <atomic> #include <thread> #include <condition_variable> #include <mutex> void foo1 (int a, std::mutex& mutex_for_a) { std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; std::unique_lock<std::mutex> lock_for_b (mutex_for_b) ; a = 1 ; b = 1 ; lock_for_a.unlock (); lock_for_b.unlock (); }; void foo2 (int a, std::mutex& mutex_for_a) {{ std::unique_lock<std::mutex> lock_for_b (mutex_for_b) ; std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; a = 2 ; b = 2 ; lock_for_a.unlock (); lock_for_b.unlock (); }; int main () { std::mutex mutex_for_a; std::mutex mutex_for_b; std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; std::unique_lock<std::mutex> lock_for_b (mutex_for_b) ; int a = 0 ; int b = 0 ; lock_for_a.unlock (); std::thread thread1 (foo1, a) ; std::thread thread2 (foo2, a) ; lock_for_a.lock (); a = 3 ; b = 3 ; std::cout << a << std::endl; }
해결 방법:
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 39 40 41 42 43 44 45 46 47 48 #include <atomic> #include <thread> #include <condition_variable> #include <mutex> void foo1 (int a, std::mutex& mutex_for_a) { std::unique_lock<std::mutex> lock_for_a (mutex_for_a) ; std::unique_lock<std::mutex> lock_for_b (mutex_for_b) ; a = 1 ; b = 1 ; lock_for_b.unlock (); lock_for_a.unlock (); }; void foo2 (int a, std::mutex& mutex_for_a) {{ std::unique_lock<std::mutex> lock_for_a (mutex_for_b) ; std::unique_lock<std::mutex> lock_for_b (mutex_for_a) ; a = 2 ; b = 2 ; lock_for_b.unlock (); lock_for_a.unlock (); }; int main () { std::mutex mutex_for_a; std::mutex mutex_for_b; std::unique_lock<std::mutex> lock_for_a (mutex_for_a, std::defer_lock) ; std::unique_lock<std::mutex> lock_for_b (mutex_for_b, std::defer_lock) ; std::lock_guard<>(lock_for_a, lock_for_b); int a = 0 ; int b = 0 ; lock_for_a.unlock (); std::thread thread1 (foo1, a) ; std::thread thread2 (foo2, a) ; lock_for_a.lock (); a = 3 ; b = 3 ; std::cout << a << std::endl; }