1. 쓰레드 기본
- std::thread : thread 객체를 생성 후 인자에 함수(정확히는 Callable)를 전달함
- thread에 함수의 인자를 전달하려면, 첫 번째 인자인 함수 뒤에 있는 두 번째 인자부터 그 함수의 인자를 전달하면 됨
- 복사 불가능하며(not copyable), 소유권 이전만이 가능함(movable)
- join / detach : join은 해당 thread가 끝날 때까지 현재 thread가 기다리며, detach는 백그라운드에서 독립적으로 실행되며 join이 불가능해짐
2. Mutex
- thread 간 배타적으로 소유되는 리소스
- 데이터 경합에 대한 보호를 쉽게 하고, execution agents 간 안전한 데이터 동기화를 가능하게 함
- 표준 Mutex 클래스
- std::mutex : 일반적인 lockable한 mutex
- std::recursive_mutex : 재귀적으로 lock이 가능한 mutex. Window API나 Critical Section의 거동과 같음
- std::timed_mutex : 재귀적으로 lock이 불가능한, 시간 제한이 붙는 mutex
- std::recursive_timed_mutex : 재귀적으로 lock이 가능한 시간 제한이 존재하는 mutex. lock 처리를 제한 시간동안 진행함
- shared_mutex
- 여러 thread가 동시에 읽기를 가능하게 하지만, 쓰기는 하나의 thread만 가능하게 해주는 read-write lock
- 즉 읽기 작업은 병렬로, 쓰기 작업은 단독으로 처리할 수 있게 함
- 공유 락(shared lock) : 여러 thread가 동시에 lock_shared 호출이 가능하며, 모두가 읽기만 하는 상황에서 병렬성을 높일 수 있음
- 배타 락(exclusive lock) : 단 하나의 thread만 lock을 통해 진입이 가능하며, 쓰기 중에는 다른 thread의 읽기/쓰기가 모두 차단됨
- Lock 클래스 : Lockable한 오브젝트를 관리하는 RAII 클래스
- lock 후에 unlock를 호출하지 않으면 mutex가 해제되지 않아 deadlock이 발생할 수 있음
- 즉 lock 후에 unlock을 명시적으로 호출해야 문제가 발생하지 않으므로 Lock 클래스를 사용하는 것이 안전함
- std::lock_guard<Mutex>, std::unique_lock<Mutex>가 표준 규격에 정의되어 있음
- lock_guard는 단순 잠금-해제 용도지만, unique_lock은 더 다양한 기능들을 제공하며, condition variable과 같이 사용할 수 있음
3. Future/Promise
- 병행 프로그래밍의 promise/future 패턴을 실현함
- 데이터를 만드는 측은 promise를, 받는 측은 future를 실현함
- std::promise<R>
- set_value : 비동기 처리의 결과로서 값을 설정
- set_exception : 비동기 처리의 결과로서 예외를 설정
- std::future<R>
- get : 비동기 처리의 결과를 획득하며, promise에서 예외가 설정된 경우 get 호출에서 원래의 예외가 재송출됨
- wait : 비동기 처리 결과가 promise에 설정되기까지 block한다
4. Condition Variable
- 조건 변수 : thread 간에 조건의 충족을 기다리고 알리는 데 사용하는 동기화 메커니즘
- 핵심 함수
- wait(unique_lock<mutex>&)
- wait(unique_lock<mutex>&, Predicate)
- notify_one() : 대기 중인 thread 하나를 깨움
- notify_all() : 대기 중인 모든 thread를 깨움
- 동작 방식 : wait(lock, pred) 형태로 사용됨
- pred가 false인 경우 lock을 풀고 thread를 대기 상태로 전환
- 다른 thread가 notify_one, notify_all을 호출 시 다시 lock을 획득하고 pred 검사
- pred가 true가 되면 최종적으로 wait가 반환됨