C++ 何时使用递归互斥锁?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2415082/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:25:19  来源:igfitidea点击:

When to use recursive mutex?

c++multithreadingrecursionmutexrecursive-mutex

提问by jasonline

I understand recursive mutex allows mutex to be locked more than once without getting to a deadlock and should be unlocked the same number of times. But in what specific situations do you need to use a recursive mutex? I'm looking for design/code-level situations.

我知道递归互斥锁允许互斥锁被锁定不止一次而不会陷入死锁,并且应该被解锁相同的次数。但是在什么特定情况下需要使用递归互斥锁呢?我正在寻找设计/代码级情况。

采纳答案by Antti Huima

For example when you have function that calls it recursively, and you want to get synchronized access to it:

例如,当您有递归调用它的函数时,您希望同步访问它:

void foo() {
   ... mutex_acquire();
   ... foo();
   ... mutex_release();
}

without a recursive mutex you would have to create an "entry point" function first, and this becomes cumbersome when you have a set of functions that are mutually recursive. Without recursive mutex:

如果没有递归互斥锁,您必须先创建一个“入口点”函数,当您有一组相互递归的函数时,这会变得很麻烦。没有递归互斥锁:

void foo_entry() {
   mutex_acquire(); foo(); mutex_release(); }

void foo() { ... foo(); ... }

回答by comonad

Recursive and non-recursive mutexes have different use cases. No mutex type can easily replace the other. Non-recursive mutexes have less overhead, and recursive mutexes have in some situations useful or even needed semantics and in other situations dangerous or even broken semantics. In most cases, someone can replace any strategy using recursive mutexes with a different safer and more efficient strategy based on the usage of non-recursive mutexes.

递归和非递归互斥锁有不同的用例。没有互斥类型可以轻松替换其他类型。非递归互斥锁的开销较小,而递归互斥锁在某些情况下具有有用甚至需要的语义,而在其他情况下则具有危险甚至损坏的语义。在大多数情况下,有人可以根据非递归互斥锁的使用,用另一种更安全、更有效的策略替换任何使用递归互斥锁的策略。

  • If you just want to exclude other threads from using your mutex protected resource, then you could use any mutextype, but might want to use the non-recursive mutex because of its smaller overhead.
  • If you want to call functions recursively, which lock the same mutex, then they either
    • have to use one recursive mutex, or
    • have to unlock and lock the same non-recursive mutex again and again (beware of concurrent threads!) (assuming this is semantically sound, it could still be a performance issue), or
    • have to somehow annotate which mutexes they already locked (simulating recursive ownership/mutexes).
  • If you want to lock several mutex-protected objects from a set of such objects, where the sets could have been built by merging, you can choose
    • to use per object exactly one mutex, allowing more threads to work in parallel, or
    • to use per object one referenceto any possibly sharedrecursive mutex, to lower the probability of failing to lock all mutexes together, or
    • to use per object one comparable referenceto any possibly sharednon-recursive mutex, circumventing the intent to lock multiple times.
  • If you want to release a lock in a different thread than it has been locked, then you have to use non-recursive locks (or recursive locks which explicitly allow this instead of throwing exceptions).
  • If you want to use synchronization variables, then you need to be able to explicitly unlock the mutexwhile waiting on any synchronization variable, so that the resource is allowed to be used in other threads. That is only sanely possible with non-recursive mutexes, because recursive mutexes could already have been locked by the caller of the current function.
  • 如果您只想排除其他线程使用受互斥锁保护的资源,那么您可以使用任何互斥锁类型,但可能希望使用非递归互斥锁,因为它的开销较小。
  • 如果要递归调用锁定相同互斥锁的函数,则它们要么
    • 必须使用一个递归互斥锁,或者
    • 必须一次又一次地解锁和锁定同一个非递归互斥体(注意并发线程!)(假设这在语义上是合理的,它仍然可能是一个性能问题),或者
    • 必须以某种方式注释他们已经锁定的互斥锁(模拟递归所有权/互斥锁)。
  • 如果您想从一组此类对象中锁定多个受互斥锁保护的对象(这些对象可以通过合并构建),您可以选择
    • 为每个对象只使用一个 mutex,允许更多线程并行工作,或
    • 为每个对象使用一个对任何可能共享的递归互斥锁的引用,以降低无法将所有互斥锁锁定在一起的概率,或
    • 为每个对象使用一个对任何可能共享的非递归互斥锁的可比较引用,从而绕过多次锁定的意图。
  • 如果要在与锁定不同的线程中释放锁,则必须使用非递归锁(或明确允许这样做而不是抛出异常的递归锁)。
  • 如果你想使用同步变量,那么你需要能够在等待任何同步变量的同时显式解锁互斥锁,这样资源才能被其他线程使用。只有使用非递归互斥锁才有可能做到这一点,因为递归互斥锁可能已经被当前函数的调用者锁定。

回答by FormerAtariUser

If you want to see an example of code that uses recursive mutexes, look at the sources for "Electric Fence" for Linux/Unix. 'Twas one of the common Unix tools for finding "bounds checking" read/write overruns and underruns as well as using memory that has been freed, before Valgrindcame along.

如果您想查看使用递归互斥锁的代码示例,请查看 Linux/Unix 的“Electric Fence”的源代码。在Valgrind出现之前,它是一种常见的 Unix 工具,用于查找“边界检查”读/写溢出和欠载以及使用已释放的内存。

Just compile and link electric fence with sources (option -g with gcc/g++), and then link it with your software with the link option -lefence, and start stepping through the calls to malloc/free. http://elinux.org/Electric_Fence

只需编译并链接电栅栏与源代码(选项 -g 和 gcc/g++),然后使用链接选项 -lefence 将其与您的软件链接,并开始逐步调用 malloc/free。 http://elinux.org/Electric_Fence

回答by Doktor Schrott

I encountered the need for a recursive mutex today, and I think it's maybe the simplest example among the posted answers so far: This is a class that exposes two API functions, Process(...) and reset().

我今天遇到了递归互斥锁的需求,我认为这可能是迄今为止发布的答案中最简单的例子:这是一个公开两个 API 函数的类,Process(...) 和 reset()。

public void Process(...)
{
  acquire_mutex(mMutex);
  // Heavy processing
  ...
  reset();
  ...
  release_mutex(mMutex);
}

public void reset()
{
  acquire_mutex(mMutex);
  // Reset
  ...
  release_mutex(mMutex);
}

Both functions must not run concurrently because they modify internals of the class, so I wanted to use a mutex. Problem is, Process() calls reset() internally, and it would create a deadlock because mMutex is already acquired. Locking them with a recursive lock instead fixes the problem.

这两个函数不能同时运行,因为它们修改了类的内部结构,所以我想使用互斥锁。问题是,Process() 在内部调用 reset(),它会造成死锁,因为 mMutex 已经被获取。用递归锁锁定它们可以解决问题。

回答by Michael Burr

It would certainly be a problem if a thread blocked trying to acquire (again) a mutex it already owned...

如果一个线程试图获取(再次)它已经拥有的互斥锁而被阻塞,那肯定会出现问题......

Is there a reason to not permit a mutex to be acquired multiple times by the same thread?

是否有理由不允许同一线程多次获取互斥锁?