简单的问题 - 基本上,我是否必须解锁互斥锁,或者我可以简单地使用范围运算符并且互斥锁会自动解锁?
即:
{
pthread_mutex_lock (&myMutex);
sharedResource++;
} // my mutex is now unlocked?
或者我应该:
{
pthread_mutex_lock (&myMutex);
sharedResource++;
pthread_mutex_unlock (&myMutex);
}
答案 0 :(得分:17)
在您的示例中,互斥锁不会超出范围;并且编译器无法知道特定函数需要在作用域的末尾调用,因此第一个示例不解锁互斥锁。
如果您正在使用(容易出错的)函数来锁定和解锁互斥锁,那么您需要确保始终调用unlock()
- 即使受保护的操作会抛出异常。
执行此操作的最佳方法是使用RAII类来管理锁定,就像使用后需要释放的任何其他资源一样:
class lock_guard {
public:
explicit lock_guard(mutex & m) : m(m) {mutex_lock(m);}
~lock_guard() {mutex_unlock(m);}
lock_guard(lock_guard const &) = delete;
void operator=(lock_guard &) = delete;
private:
mutex & m;
};
// Usage
{
lock_guard lock(myMutex);
shared_resource++;
} // mutex is unlocked here (even if an exception was thrown)
在现代C ++中,请使用std::lock_guard
或std::unique_lock
。
答案 1 :(得分:2)
使用RAII范围方法要好得多,因为它可以保证即使面对例外或提前返回,互斥锁也将始终处于解锁状态。
如果您可以访问C ++ 11,可以考虑使用std::atomic<int>
,在这种情况下,您不需要将其锁定为增量。
答案 2 :(得分:2)
在这种情况下,当此代码超出范围时,不会解锁互斥锁。
RAII之后的Mutex锁定器使用的事实是,当非堆分配的对象超出范围时,会自动调用析构函数。然后,一旦锁定互斥锁的对象超出范围,它就会解锁互斥锁。在代码的情况下,在大括号的范围内没有分配任何对象,因此一旦范围结束,就没有可能解锁互斥锁。
例如,使用Qt库中的QMutexLocker
,您可以确保在范围结束时解锁您的互斥锁:
{
QMutexLocker locker(myMutex);
if(checkSomething())
{
return;
}
doSomething();
}
此代码类似于:
{
mutex_lock(myMutex);
if(checkSomething())
{
mutex_unlock(myMutex);
return;
}
doSomething();
mutex_unlock(myMutex);
}
虽然Brian Neal指出,但它并不能安全地处理checkSomething()
和doSomething()
抛出异常的情况。
Qt QMutexLocker
的替代方案是STD的std::lock_guard。