避免在单个线程中出现死锁

时间:2013-12-25 19:38:29

标签: c++ multithreading glib

我遇到以下问题:我需要保护一个类,以防止来自不同线程的同时访问。该类有两个方法:lock()和unlock()使用(g_mutex_lock / g_mutex_unlock和每个对象GMutex)。现在锁定方法如下所示:

void Object::method()
{
    lock();
    // do stuff modifying the object
    unlock();
}

现在让我们假设我有两个这种类型的方法,method1()method2()我会一个接一个地调用:

object.method1();
// but what if some other thread modifies object in between
object.method2();

我尝试在此块之前锁定对象并再次解锁它,但在这种情况下 即使使用单个线程也存在死锁,因为GMutex不知道它已被同一个线程锁定。解决方案是修改方法以接受额外的bool以确定对象是否已被锁定。但是有更优雅的概念吗?或者这是关于设计概念的一个缺点吗?

3 个答案:

答案 0 :(得分:6)

其他响应和注释中提到的递归互斥解决方案可以正常工作,但根据我的经验,它会导致代码更难维护,因为一旦切换到递归互斥锁,就很容易滥用它并锁定到处都是。

相反,我更喜欢重新组织代码,以便锁定一次就足够了。在您的示例中,我将按如下方式定义类:

class Object {
public:
    void method1() {
        GMutexLock scopeLock(&lock);
        method1_locked();
    }
    void method2() {
        GMutexLock scopeLock(&lock);
        method2_locked();
    }
    void method1_and_2() {
        GMutexLock scopeLock(&lock);
        method1_locked();
        method2_locked();
    }

private:
    void method1_locked();
    void method2_locked();

    GMutex lock;
};

您的方法的“锁定”版本是私有的,因此只能从类中访问它们。在没有锁定的情况下,该类负责从不调用它们。

从外面你可以选择三种方法来调用,具体取决于你想要运行的方法。

请注意,我所做的另一项改进是不使用显式锁定原语,而是间接使用范围来锁定和解锁。这就是GMutexLock的作用。此类的示例实现如下:

class GMutexLock {
private:
    GMutex* m;

    GMutexLock(const GMutexLock &mlock); // not allowed
    GMutexLock &operator=(const GMutexLock &); // not allowed

public:
    GMutexLock(GMutex* mutex) {
        m = mutex;
        g_mutex_lock(m);
    }

    ~GMutexLock() {
        g_mutex_unlock(m);
    }
};

答案 1 :(得分:4)

查找“递归互斥”或“reentrant mutex”(与您现在使用的非递归互斥锁相比)。这些可以实现你想要的。有些人不是递归互斥体的粉丝,并觉得它们可以实现混乱的设计。

请注意,递归互斥锁不能锁定在一个线程上,而在另一个线程上解锁。

答案 2 :(得分:0)

我个人永远不会使用递归互斥体(尤其如此)。

我会做一些私有函数,它们不会锁定互斥锁并在公共函数的实现中锁定它们。