2014-10-30 207 views
2

我在我的程序中遇到了死锁问题。所以我一直在阅读关于锁的问题,但问题是大多数信息不一致或者没有定义平台。在Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex)最接受的回答说:递归和非递归锁(互斥锁)

由于递归互斥有主人翁意识,在线程 抓住互斥必须是释放互斥锁在同一个线程。在 非递归互斥体的情况下,没有所有权意识,任何线程通常都可以释放该互斥体,无论哪个线程本来是 接受互斥体。在许多情况下,这种类型的“互斥体”实际上更多的是信号量动作,在这种情况下,您不一定要使用互斥体作为排斥设备,而是将其用作两个或更多线程之间的同步或信号设备 。

在评论中,人们说这是不正确的,没有提及它。所以...

1)如果我在线程A中锁定了一个非递归互斥锁,线程B可以解锁它而无需获取锁?

2)如果线程A在一个非递归互斥体中进行了锁定,并且线程B调用了锁定,那么线程B是否等待,直到锁定被释放以获得锁定或将抛出一个异常?这个递归互斥体的情况呢? (也可以在其他问题中进行讨论,其中没有可以作出的正确结论)

3)当使用递归锁时,在进程终止时,是否必须释放所有递归锁? (取决于过程结束的地方没有发生)

4)当谨慎使用递归和非递归锁的组合时,我会注意哪些问题?

PS:只使用windows平台和std::thread

回答

6

通过阅读关于Reentrant Mutexes的维基,我认为你会得到很大的帮助。我同意另一条线索中的评论;被接受的答案是错误的,或者至少非常不好地解释它的观点。

所有Mutexes都有一个所有权的概念。这就是他们与Semaphore不同的原因。锁定互斥锁的线程始终是需要解锁的线程,这也是互斥锁可能导致死锁的原因之一,也是为什么它们能够达到其预期目的(至相互排除访问特定代码块)。

那么Recursive/Reenrant和普通Mutex有什么不同?一个递归互斥锁可以被同一个线程多次锁定。引用维基:

递归锁(也称为递归线程互斥锁)是允许线程递归获取它所持有的同一个锁的那些锁。请注意,此行为与普通锁不同。在正常情况下,如果已经拥有正常锁的线程尝试再次获取相同的锁,则它会死锁。

这是两个互斥体类型之间的完整区别。实质上,如果你在递归方法中放置一个互斥锁并且该方法在释放互斥体之前递归,那么你需要递归互斥锁。否则,在第一次递归之后,将会发生即时死锁,因为无法再次获取该锁。

真的,这是只有原因使用递归互斥;大多数其他情况下,如果获得相同线程而不释放它的同一个线程,可能会重构为正确获取/释放锁而不需要递归互斥锁。这样做会更安全。递归函数自然会冒泡并释放递归互斥锁上的每个锁,假设RAII,在其他情况下,您可能无法充分释放互斥锁并仍然陷入死锁状态。

因此,要回答你的具体问题:

  1. 没有,除非你的互斥系统特别允许这
  2. 是在这两种情况一般,虽然这同样是特定至于阻断/投掷互斥的实现。几乎所有的系统我用过刚刚块(这就是为什么非递归互斥体的僵局,如果在同一个线程锁两次没有自由)
  3. 是,通常情况下,虽然通常他们会被释放,假设正确RAII和进程终止正常。进程在保持锁定状态下非正常终止可能有点难以理解。
  4. 看到我上面的解释。具体来说,提请注意从维基如下:

注意,递归锁是说当且仅当它已经得到的次数相匹配的次数已释放被释放由所有者线程。

+0

_The接受的答案是错误的,或者至少是非常说明其一点,“锁定互斥体可能比它与非递归互斥种所有者以外的其他线程来解锁”非常poorly._它似乎你现在接受的答案可能会使用警卫。 – bvj 2017-12-27 02:13:46

0

真的,你应该写一个简单的程序来测试这些案件。

  1. 假设我们正确使用互斥锁,另一个线程不应该解锁互斥锁。任何线程都可能解锁互斥锁。 (编辑:我从来没有尝试解锁,另一个线程互斥这会破坏目的)这将导致竞争条件。

  2. 考虑代码:

    void criticalSection(){ 
        pthread_mutex_lock(&mutex) 
        //dostuff 
        pthread_mutex_unlock(&mutex) 
    } 
    

    如果有两个线程,线程A和B,和线程A首先进入的功能,它获取锁和确实的东西。如果线程B的状态时,线程A还是在功能上,它会被锁定。线程A执行时

    pthread_mutex_unlock(&mutex) 
    

    线程B现在将被“唤醒”并开始执行任务。

  3. 我想你可以做任何你想做的事情,但是如果有递归锁意味着一个线程仍然在做某件事,你可能想等待它完成。

  4. 我想你正在寻找没有竞争条件多线程应用程序。:-)

+2

几乎总是在多线程中,最好是编写正确的代码,而不是基于行为而是基于文档。所以“你应该写一个简单的程序来测试这些情况”恕我直言是一个不好的建议,因为它很容易引起误解。 – Slava 2014-10-30 14:49:54

+0

那么,在这种情况下,只是看看另一个线程是否可以解锁锁定的互斥锁。但是,如果这甚至是可能的,它不应该发生在你的代码 – 2014-10-30 14:55:25

+0

“这只是一个看看另一个线程是否可以解锁锁定互斥体的问题”,以防止旋转锁定,它可以很容易地在测试程序中“工作”。这并不意味着它应该以这种方式使用。 – Slava 2014-10-30 15:07:17

1

你指的是POSIX互斥体讨论,但Windows不支持POSIX,你使用C++标准线程原语,细节可能有所不同。

所以最好先检查标准库文件,例如C++标准为unlock明确规定:

要求:调用线程将拥有互斥。

1

以下是在Linux的pthread_mutex_lock手册页,

型pthread_mutex_t的变量也可以使用常量PTHREAD_MUTEX_INITIALIZER(快速互斥),THREAD_RECURSIVE_MUTEX_INITIALIZER_NP(递归互斥)和PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP静态初始化, (用于错误检查互斥体)。

error checking'' and递归'互斥体,调用pthread_mutex_unlock实际上在检查该互斥锁于入口的运行时间,而且它是由现在要求调用pthread_mutex_unlock相同的线程锁定。如果这些条件不符合,则返回错误代码并且互斥量保持不变。 ``快速''互斥锁不执行此类检查,因此允许锁定的互斥锁由非所有者的线程解锁。这是不可移动的行为,不能依赖。

看来,