6

简单的问题 - 基本上,我是否必须解锁互斥锁,或者我可以简单地使用范围运算符并且互斥锁会自动解锁?

IE:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++; 
} // my mutex is now unlocked?

或者我应该:

{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++;
    pthread_mutex_unlock (&myMutex);
}
4

3 回答 3

18

在您的示例中,互斥锁并没有超出范围;并且编译器无法知道特定函数需要在作用域末尾调用,因此第一个示例不会解锁互斥锁。

如果您使用(容易出错的)函数来锁定和解锁互斥锁,那么您需要确保始终调用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_guardor std::unique_lock

于 2013-07-18T16:52:46.287 回答
2

使用 RAII 范围方法要好得多,因为它可以保证即使在遇到异常或提前返回时,互斥锁也将始终处于解锁状态。

如果您可以访问 C++11,但您可能会考虑使用 astd::atomic<int>而不是在这种情况下,您不需要锁定它来增加。

于 2013-07-18T16:46:25.920 回答
2

在这种情况下,当此代码超出范围时,不会解锁互斥锁。

RAII 之后的互斥锁使用这样一个事实,即当非堆分配的对象超出范围时会自动调用析构函数。一旦锁定互斥锁的对象超出范围,它就会解锁互斥锁。对于您的代码,在大括号的范围内没有分配任何对象,因此一旦范围结束,互斥锁就不可能被解锁。

例如,使用QMutexLockerQt 库,您可以确保在范围结束时解锁互斥锁:

{
    QMutexLocker locker(myMutex);
    if(checkSomething())
    {
        return;
    }
    doSomething();
}

此代码类似于:

{
    mutex_lock(myMutex);
    if(checkSomething())
    {
        mutex_unlock(myMutex);
        return;
    }
    doSomething();
    mutex_unlock(myMutex);
}

尽管正如 Brian Neal 指出的那样,它并不能安全地处理 wherecheckSomething()doSomething()throw 异常的情况。

Qt 的替代方案QMutexLocker是 STD 的std::lock_guard

于 2013-07-18T16:51:42.630 回答