【问题标题】:Cost of mutex,critical section etc on WindowsWindows 上的互斥锁、临界区等成本
【发布时间】:2013-08-07 12:50:13
【问题描述】:

我在某处读到,互斥锁的开销并不大,因为上下文切换仅在争用情况下发生。

在 Linux 中也称为 Futex。

同样的事情在 Windows 中也适用吗?关键部分是否更适合 Linux 中的互斥锁。

根据我收集到的信息,与 Mutex 相比,Critical Sections 提供了更好的最佳性能,这是否适用于每种情况?

在 Windows 中是否存在互斥体比临界区快的极端情况。

假设只有一个进程线程正在访问互斥锁(只是为了消除关键部分的其他好处)

添加信息:操作系统 windows 服务器,
语言 C++

【问题讨论】:

  • 当然,如果您的应用程序经常发生争用,您可能会重新考虑互斥锁没有太多开销的想法。

标签: c++ windows multithreading mutex critical-section


【解决方案1】:

考虑到Critical SectionsMutexes 的特定目的,我认为您不能就成本提出问题,因为当您需要多个线程处理相同的数据时,您没有太多选择。显然,如果您只需要增加/减少一个数字,您可以在 volatile 数字上使用 Interlocked*() 函数,您就可以开始了。但是对于更复杂的事情,您需要使用同步对象。

Synchronization Objects available on Windows^ 上开始阅读。所有功能都列在那里,很好地分组并正确解释。 有些仅适用于 Windows 8。

关于您的问题,Critical SectionsMutexes 更便宜,因为它们被设计为在相同的过程中运行。阅读 this^this^ 或只是以下引用.

临界区对象提供的同步类似于互斥对象提供的同步,但临界区只能由单个进程的线程使用。事件、互斥体和信号量对象也可以在单进程应用程序中使用,但临界区对象为互斥同步(特定于处理器的测试和设置指令)提供了更快、更有效的机制。与互斥对象一样,临界区对象一次只能由一个线程拥有,这对于保护共享资源不被同时访问非常有用。与互斥对象不同,无法判断临界区是否已被放弃。

我使用Critical Sections 进行同一进程同步,Mutexes 进行跨进程同步。 只有当我真的需要知道一个同步对象是否被放弃时,我才使用 Mutexes相同的过程。

所以,如果您需要一个同步对象,问题不在于成本是多少,而在于哪个更便宜 :) 除了内存损坏之外别无选择。

PS:可能有像 one mentioned in the selected answer here^ 这样的替代方案,但我总是选择核心平台特定功能与跨平台性。它总是更快! 所以如果你使用 Windows,请使用 Windows 的工具 :)

更新

根据您的需要,您可以通过尝试在线程中完成尽可能多的自包含工作并仅在最后或不时组合数据来减少对同步对象的需求。

愚蠢的例子获取一个 URL 列表。你需要抓取它们并分析它们。

  1. 加入一堆线程并开始从输入列表中一个一个地挑选 URL。对于您的每一个过程,您在执行过程中都会集中结果。实时且酷
  2. 或者您可以将线程放入每个线程中,每个线程都具有输入 URL 的一部分。这消除了同步选择过程的需要。您将分析结果存储在线程中,最后只组合一次结果。或者说每 10 个 URL 一次。不是为他们每个人。这将大大减少同步操作。

因此可以通过选择正确的工具并考虑如何降低锁定和解锁来降低成本。但成本无法消除 :)

PS:我只考虑 URL :)

更新 2:

需要在项目中进行一些测量。结果非常令人惊讶:

  • std::mutex 最贵。 (跨平台的价格)
  • Windows 原生 Mutexstd 快 2 倍。
  • Critical Section本机 Mutex 快 2 倍。
  • SlimReadWriteLockCritical Section 的 +-10%。
  • 我自制的 InterlockedMutex (spinlock)Critical Section 快 1.25 倍 - 1.75 倍。

【讨论】:

  • 我知道我无法消除成本,但我只是在想关键部分或互斥锁是否适合我。谢谢你的信息
  • -1 表示易失性。您永远不应该仅依靠 volatile 进行线程处理。要么使用 atomic,要么将 volatile 与内存栅栏结合使用。否则,例如,您的 volatile 访问最终可能会在 Linux / gcc 上重新排序。
  • 这不是真的,没有其他选择。 Windows 实现是系统范围的,由权限\访问列表管理,互斥锁是进程之间的同步接口,它们不等同于经典互斥锁。临界区与线程优先级管理器接口,Windows 中的线程有自己的内存区域 - 线程本地存储。对这些函数的调用涉及相当多的 dll 调用。如果不需要与操作系统进行深入而普遍的交互,出于性能考虑,可以使用非 API、用户空间互斥锁、自旋锁、原子等。
【解决方案2】:

在 Windows 8 上使用 std::mutex 我通常通过使用我自己定制的自旋锁获得 3-4 倍的改进(在非竞争情况下):

基于互斥锁

auto time = TimeIt([&]() {
for (int i = 0; i < tries; i++) {
    bool val = mutex.try_lock();
    if (val) {
        data.value = 1;
    }
}

});

自制无锁

time = TimeIt([&]() {
    for (int i = 0; i < tries; i++) {
        if (!guard.exchange(true)) {
            // I own you
            data.value = 1;
            guard.store(true);
        }
    }
});

在 x86 上进行测试。

我还没有弄清楚 std::mutex 在 windows 上使用什么下划线,因为它会生成很多代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 2010-10-22
    • 2021-04-16
    • 2020-04-10
    • 2016-10-25
    • 2010-10-27
    相关资源
    最近更新 更多