当互斥锁超出作用域时,它是否会被解锁?

6

简单的问题 - 我是否需要解锁互斥锁,还是可以直接使用作用域运算符,互斥锁会自动解锁?

例如:

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

或者我应该:
{ 
    pthread_mutex_lock (&myMutex); 
    sharedResource++;
    pthread_mutex_unlock (&myMutex);
}

5
你使用了哪种互斥锁实现?除非你的互斥锁实现支持RAII,否则你可能需要显式解锁互斥锁。 - Void
感谢@Void。我正在使用pthread.h,即pthread_mutex_unlock()和pthread_mutex_lock()。RAII是什么? - Amit Nayar
3
@AmitNayar:请查看http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization。这可能是C++中最重要的习惯用语,因为几乎不使用它就难以正确地管理内存和其他动态资源。 - Mike Seymour
3
请修改您的代码,以展示您正在使用 pthread_mutex_lock 等功能。这非常重要。没有人知道 mutex_lock 是什么意思。 - Brian Neal
3个回答

18

在你的示例中,mutex 没有超出作用域; 并且编译器无法知道特定函数需要在作用域结束时调用,因此第一个示例不会释放 mutex。

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


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

2
如果考虑到异常,这两个代码片段是不等价的。要使它们等价,您需要在checkSomethingdoSomething周围添加try/catch块。这只是更喜欢使用RAII的第一个示例的另一个原因。 :) - Brian Neal
好观点,@BrianNeal。我已经更新了答案。我强调不使用异常,因为在Qt基础设施中它们并不是必需的,所以我很容易忘记其他人需要考虑它们。 - Cory Klein

2
使用RAII作用域方法更好,因为它保证即使面临异常或早期返回,互斥锁也将始终被解锁。
如果您可以访问C++11,那么您可以考虑使用std::atomic<int>,在这种情况下,您不需要锁定它来递增。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接