글
std::mutex의 lock 멤버 함수의 설명을 보면 lock을 호출한 함수에서 unlock을 호출하지 않은 상태에서 또 다시 lock을 호출하면 알 수 없는 동작을 한다고 되어 있습니다.
스레드에서 lock을 호출한 후 다시 lock을 호출하는 경우는 아래와 같은 경우입니다.
class buffer {
list<int> queue;
std::mutex mut;
public:
bool empty() {
std::lock_guard<std::mutex> lock(mut);
return queue.empty();
}
...
int pop() throw(out_of_range) {
std::lock_guard <std::mutex> lock(mut);
while (empty())
{
……..
}
int tmp = queue.front();
queue.pop_front();
return tmp;
}
};
코드 출처 : http://d.hatena.ne.jp/hidemon/20081218/1229555003
위의 buffer 클래스를 보면 pop() 함수를 호출하면 먼저 mutex로 락을 건 다음 empty() 함수를 호출하는데 empty도 같은 mutex를 사용하여 lock을 걸고 있습니다. 즉 이미 락을 건 mutex에 또 lock을 걸고 있습니다. 이런 경우 데드락 상황에 빠질 수가 있습니다. 이 문제를 풀기 위해서는 recursive_mutex를 사용합니다.
recursive_mutex는 같은 스레드에서 lock을 건 후 또 다시 lock을 걸어도 괜찮습니다. 단 lock을 건 횟수만큼 꼭 unlock을 호출해야 합니다.
그럼 아래는 위의 buffer 클래스를 recursive_mutex를 사용하여 올바르게 수정한 것 입니다. 단순하게 std::mutex를 std::recursive_mutex로 바꾸면 됩니다.
(std::mutex와 std::recursive_mutex의 사용 방법은 같습니다)
class buffer {
list<int> queue;
std::recursive_mutex mut;
public:
bool empty() {
std::lock_guard<std::recursive_mutex> lock(mut);
return queue.empty();
}
...
int pop() throw(out_of_range) {
std::lock_guard <std::recursive_mutex> lock(mut);
while (empty())
{
……..
}
int tmp = queue.front();
queue.pop_front();
return tmp;
}
};
댓글