공유 객체 동기화하기


병렬 프로그래밍을 해보면 싫든 좋든 여러 스레드에서 동시에 하나의 자원(공유 자원)에 접근하는 경우가 발생한다.

 

 

 

이것은  Thread1 스레드에서 출력 스트림 결과를 다 출력하기도 전에 Thread2가 출력 스트림을 사용하여 발생한 문제다. 이를 가리켜 '데이터 경합(Data-race)'이라고 한다.

 

바로 이런 부분이 병렬 프로그래밍의 어려움 중 하나다. 여러 스레드에서 하나의 공유 자원을 사용할 때 타이밍에 의해 순서대로 사용할 수도 있고 그렇지 못할 수도 있어서 버그가 언제 어떻게 발생할지 알기 힘들다. 병렬 프로그래밍을 할 때는 언제나 공유 자원을 최소한으로 하고 꼭 동기화 객체를 사용하여 여러 스레드가 동시에 사용하지 못하도록 해야 한다.

 

 

 

 

 

mutex 사용

 


 

 

 

 

<공유 객체 사용 버그 수정 예제>

 

 

 

 

 

 

try_lock


std::mutex 에는 'lock'과 'unlock'이외에 'try_lock'이라는 멤버 함수가 있다. 앞서 언급했듯, 스레드에서 lock을 호출할 때 다른 스레드가 이미 lock을 호출했다면 그 스레드가 unlock을 호출할 때까지 대기해야 한다. try_lock은 이처럼 다른 스레드가 락을 걸어서 자원을 사용할 수 없을 때 다른 일을 하고 싶은 경우 사용한다.

 

 

 

 

 

자동으로 락 풀기


mutex를 사용하면 복수의 스레드가 서로를 침범하지 않고 공유 자원을 사용할 수 있다. 그러나  lock을 사용하여 공유 자원에 대한 소유권을 얻은 후 실수로 unlock을 하지 않으면(락을 풀지 않으면) 아무도 그 공유 자원을 사용하지 못하는 데드락에 빠질 수 있다(데드락에 빠지면 찾기도 힘들다). 예를 들어 다음과 같은 경우 이런 실수를 하기 쉽다.

 

 

 

 

 

<lock_guard를 사용하여 자동으로 락을 걸고 해제하기 예제>

 

 

 

 

 

 

반복하여 락 걸기


mutex의 lock 멤버 함수의 설명을 보면 lock을 호출한 함수에서 unlock을 호출하지 않고 다시 lock을 호출하면 알 수 없는 동작을 한다고 되어 있다.

 

 

 

 

 

 

 

 

지정한 시간 동안만 락 시도하기


try_lock을 사용할 때 mutex의 소유권을 가질 수 있으면 true을 반환하고 그렇지 못하면 바로 false를 반환한다. 따라서 소유권을 얻고싶으면 true를 반환할 때까지 try_lock을 호출해야 한다. 그런데 조건이 없이 무한정 try_lock을 시도하는 것이 아니라 특정 시간 동안만 시도하고 성공하지 못하면 try_lock 호출을 멈추길 바라는 경우도 있을 것이다. 이럴때 사용하는것이 'timed_mutex'다.

 

timed_mutex는 mutex의 모든 기능이 있으며 추가로 함수 두개(try_lock_for 와 try_lock_until)가 더 있다. 이 함수들은 시간을 이용하여 mutex 소유를 시도하는데, 시간은 chrono를 사용하여 설정한다. 이 중에서 timed_mutex에 있는 try_lock_for는 지정한 시간 동안 락 걸기를 시도한다.

 

 

 

 

 

 

 

 

 

 

unique_lock


unique_lock 클래스는 쉽게 말해 lock_guard 클래스에 기능이 더해진 것이라고 볼 수 있다. lock_guard는 정의와 동시에 락을 걸고 파괴될 때만 락을 풀 수 있다. 그러나 unique_lock은 락을 정의와 동시에 걸 수도 있고 생성한 후 에 걸 수도 있다. 또한, 락 풀기도 원하는 대로 조절할 수 있으며 try_lock, try_lock_for, try_lock_until 등도 지원한다. 그리고 unique_lock끼리 소유한 mutext 객체를 서로교환하는 것도 가능하며 mutex 객체를 얻거나 해제할 수도 있다. 간단한 경우에는 lock_guard가 사용하기 편하지만, mutex를 세밀하게 사용하고 싶다면 unique_lock을 이용하는 것이 훨씬 더 좋다.

 

 

 

 

'C++ > STL' 카테고리의 다른 글

20. 스레드 (thread)  (0) 2017.10.31
각 STL 컨테이너를 써야할 때를 정리(차이점)  (0) 2017.10.26
19. chrono  (0) 2017.10.25
18. unordered_map  (0) 2017.10.24
17. forward_list  (0) 2017.10.23

+ Recent posts