【问题标题】:Avoid deadlock in a single thread避免单线程死锁
【发布时间】:2013-12-25 19:38:29
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 你不能使用递归互斥锁吗??

标签: c++ multithreading glib


【解决方案1】:

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

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

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);
    }
};

【讨论】:

  • GMutexLock 定义在哪里? Glib 是用 C 写的,应该不会支持 RAII 吧?
  • 其实我犯了一个错误,我找到了一个不同的 GMutex 实现,而不是 glib 的实现。我将在我的答案中添加一个示例实现,它非常简单。
  • 添加了示例 GLockMutex 实现。
  • IMO 这是一个完美的答案。
  • 嗯,它确实很容易实现,我只是想知道它是否与 glib 一起提供,非常感谢 Miguel :)
【解决方案2】:

查找“递归互斥锁”或“reentrant mutex”(相对于您现在使用的非递归互斥锁)。这些可以实现您想要的。有些人不喜欢递归互斥锁,并认为它们会导致混乱的设计。

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

【讨论】:

  • 但是 glib 是否带有递归互斥结构?我看到的只是 GStaticRecMutex。是否可以按对象使用该互斥锁?
  • “有些人不喜欢递归互斥锁,并认为它们会导致混乱的设计。”是的。并且锁定内部有锁的函数调用是他们正在谈论的混乱设计。
  • 好吧,我环顾四周,现在我很清楚递归互斥锁不是答案,即使它在这种情况下也可以工作。不过很高兴了解他们。
【解决方案3】:

我个人永远不会使用递归互斥锁(尤其是这样)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多