如何判断有另一个线程试图持有互斥锁?

Posted

技术标签:

【中文标题】如何判断有另一个线程试图持有互斥锁?【英文标题】:How to tell there is another thread trying to hold mutex? 【发布时间】:2019-08-23 06:12:39 【问题描述】:

我有一个算法,效率很关键,90% 的时间在单线程模式下运行。现在我们意识到还有另外 10% 的用例需要让它支持多线程。

可悲的是这个算法的一部分,在多线程情况下确实需要互斥锁保护。这个关键部分重复了数十亿次,因此将互斥操作保持在单线程中确实是浪费滴答声。

是否可以让一个类“感知互斥锁”,以便我们只在检测到有另一个线程在运行这个类实例时才触发互斥锁保护? c++ 的最佳实践是什么?

更新: 有些人认为问题不清楚。我的错。让我尝试更多细节。

系统有 90% 的时间在不断地扫描大量数据(想象成许多数据框),并将统计信息发送给其他线程,但没有太多细节。然后有时,如果一个 RPC 客户端得到一些有趣的东西,它会询问特定范围的框的更多信息。在盒子之间移动非常昂贵,因此这里没有随机访问。此时,客户端算法会按计划返回以获取那些特定的盒子。现在是子算法(即处理一个单独的盒子)需要互斥体(维护指针状态,边界处理和许多其他东西等)。

因为算法 90% 的时间都是单线程模式,所以我只希望它快速移动,而不是在每个盒子上获取锁。而当有客户想要寻找和使用单盒算法时,这将成为互斥锁起作用的唯一情况(我们只能在运行时知道)。

【问题讨论】:

好像here你可以找到答案。 如果没有关于该问题的更多信息,任何人都很难为您提供帮助。在modernescpp.com/index.php/… 阅读并检查您是否可以将其作为无锁代码进行。 use case we need to make it support multi-threading 是什么意思?该算法本身使用多个线程来解决一项任务。或者让你的算法线程保存,以便多个线程可以对不同的任务使用相同的算法。 您可以仅在实际需要时动态创建互斥锁,并且仅在已创建互斥锁时让算法使用它。 服务器是否可以复制单框的内容供客户端单独访问? 【参考方案1】:

一种方法是将您的代码模板化为互斥锁类型,然后在单线程情况下传递一个假互斥锁。比如这样的:

template<typename Mutex>
void foo( Mutex& mutex)

   std::unique_lock lock(mutex);
   // Do stuff

然后您可以使用和不使用互斥锁进行调用:

std::mutex mutex;
foo(mutex);

struct fake_mutex

   void lock()
   void unlock()
;

fake_mutex mutex;
foo(mutex);

编译器应该优化掉伪造的互斥锁,使其很少甚至没有代码。

【讨论】:

【参考方案2】:

这是 Read-Copy-Update (RCU) https://lwn.net/Articles/262464/ 的一个很好的用例。 RCU 最初设计需要操作系统权限(特别是禁用中断和调度),但是有一个用户空间 RCU 库 URCU https://lwn.net/Articles/573424/。

RCU 是一种无锁范例,并针对读取情况进行了大量优化。在某些情况下,当进入读取端临界区时,RCU 的特权版本会产生开销。

我怀疑您是否真的需要互斥锁,并且我认为您可以使用 RCU 代替互斥锁。但是,如果您希望保留互斥锁,您可以使用 RCU 标志作为锁定互斥锁的条件。这有点不标准,因为通常受互斥锁保护的共享数据应该始终使用互斥锁,但没关系,因为 RCU 保护与互斥锁一样好。

您必须通读大量材料才能理解 RCU,但这里有一个速成课程来解释特权 RCU 的风格。

要进入读取端临界区,CPU 会禁用中断(仅针对该 CPU)。然后 CPU 可以读取一个标志,例如if (!locked)。完成读取端临界区后,CPU 重新启用中断。

如果线程希望修改某些数据,它会写入标志locked = true 并传播写入(通过内存屏障)。此时,任何新的读取端临界区都会看到标志被锁定并避免进入临界区,但我们仍然需要担心当前正在进行的读取端临界区。因此,修改线程会在每个 CPU 上自行调度。如果另一个 CPU 处于读取端临界区,则该 CPU 上的中断将被禁用,因此在读取端临界区完成之前,修改线程将无法在该 CPU 上运行。此锁定函数返回后,可以修改数据,因为它保证了独占访问。

RCU 的替代方案是风险指针,它比 RCU 更适合偶数读/写访问情况。危险指针也是无锁的。危险指针背后的想法是让线程标记它正在使用的东西。我认为你的情况更适合 RCU,但可能有更多的库实现了危险指针。

【讨论】:

以上是关于如何判断有另一个线程试图持有互斥锁?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以确定持有互斥锁的线程?

读写锁概述

Linux 线程同步都有哪些方法?

linux多线程——互斥量实现同步

Java并发编程:死锁(含代码)

Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock