【问题标题】:Does Boost have support for Windows EnterCriticalSection API?Boost 是否支持 Windows EnterCriticalSection API?
【发布时间】:2016-01-10 20:43:03
【问题描述】:

我知道 Boost 支持 mutex 和 lock_guard,可用于实现临界区。

但是 Windows 有一个用于关键部分的特殊 API(请参阅 EnterCriticalSectionLeaveCriticalSection),它比互斥锁快很多(对于很少竞争的短代码段)。

因此我的问题是——在 Boost 中是否可以利用这个 API,并在其他平台上回退到基于 spinlock/mutex/futex 的实现?

【问题讨论】:

  • 在有用的指针中:codereview.stackexchange.com/questions/1836/… 可能会很好,这个邮件列表讨论 comments.gmane.org/gmane.comp.lib.boost.devel/54585(从 2001 年开始)
  • 对于琐事:Boost Smart Pointer、Boost Signals2、Boost Asio 和 Boost Container 包含使用 CriticalSection 的实现细节(你可以从那里删除包装器,可能)
  • 不是重复的 IMO;这个问题是关于 Boost 的,另一个问题是关于标准库的。
  • @HarryJohnston 已修复

标签: c++ windows boost critical-section


【解决方案1】:

简单的答案是否定的。

这里有一些相关背景from an old mailing list thread:

顺便说一句。我同意互斥锁是来自

的更通用解决方案

性能观点。但公平地说 - CS 在简单方面更快 设计。我相信支持他们的可能性应该在 至少 考虑在内。

这是有人指给我看的文章。结论是 只有在以下情况下,CS 才会更快:

  • 进程中的线程总数少于 8 个。
  • 您没有在后台运行。
  • 您使用的不是双处理器机器。

对我来说,这意味着简单的测试会产生良好的 CS 性能 结果,但任何现实世界的程序都更好 互斥体。

我并不反对支持 CS 实施。但是,我 最初选择不这样做的原因如下:

  • 您可以通过使用 PIMPL 获得建造和破坏命中 成语,或者您必须在 Boost.Threads 标头中包含 Windows.h, 我根本不想这样做。 (这可以通过 模拟来自 MSDN 的 CS ala OPTEX。)
  • 根据这篇研究论文,大多数程序不会受益于 CS 设计。
  • 编写一个(不可移植的)critical_section 类很简单 如果您真的可以利用它,请遵循 Mutex 模型。

就目前而言,我认为我做出了正确的选择,尽管在未来我们 可能会更改实现以使用临界区或 OPTEX。

比尔·肯普夫

【讨论】:

  • 嗯。一些搜索确实表明它可能使用 Mutex,而不是 CriticalSection。在我结束之前让我再看看
  • 在 Windows 中,临界区不是互斥体。它是一个用户模式对象,而互斥锁是一个内核模式对象。
  • 显然这个实现细节已经改变了 Boost Thread 的历史。我只能假设有充分的理由。我建议编写您自己的 BasicLockable 模型 - 一个可能会在网上流传
  • @rustyx 我知道这一点。我想强调,即使boost::mutex 被命名为“mutex”,它并不一定意味着实现使用同名的 Win32 原语。 (虽然,现在所有这些都没有实际意义)
  • 更新了我的答案,使其更具相关性。
【解决方案2】:

作为一个帮助维护 Boost.Thread 的人,以及作为一个未能将事件对象放入 Boost.Thread 的人,我认为关键部分从未被添加过,也不会被添加到 Boost 中,原因如下:

  1. 使用 boost::atomic 和 boost::condition_variable 可以非常容易地构建 Win32 临界区,因此不值得拥有一个正式的临界区。这可能是您能想象到的最复杂的一个,但非常可配置,包括准备好 constexpr(不要问!):https://github.com/ned14/boost.outcome/blob/master/include/boost/outcome/v1/spinlock.hpp#L331

    您可以简单地通过匹配(基本)可锁定概念并使用原子 compare_exchange(非 x86/x64)或原子交换(x86/x64)来构建自己的,然后在关键部分周围使用 lock_guard 抓取它。

    有些人可能会反对 win32 的关键部分不是这个。恐怕是这样:它只是在原子上旋转以进行旋转计数,然后懒惰地尝试分配一个 win32 事件对象,然后等待该对象。没什么特别的。

  2. 尽管您可能认为关键部分(真正的用户模式互斥锁)更好/更快/无论如何,但它们可能没有您想象的那么好。 boost::mutex 是 Windows 内部使用 win32 信号量作为内核等待对象的一个​​巨大重量级的东西,因为需要模拟线程取消并在通用使用上下文中表现良好。对于某些单个用例,编写一个比另一个更快的并发结构容易,但编写一个包含以下所有内容的并发结构非常非常困难:

    1. 在无竞争情况下比标准实现更快。
    2. 在轻微竞争的情况下比标准实现更快。
    3. 在竞争激烈的情况下比标准实现更快。

    即使您管理了上述所有三项,但这仍然不够:您还需要对最坏情况的进展顺序进行一些保证,因此锁定、等待和解锁的某些模式是否会产生可预测的结果。这就是为什么线程工具在狭窄的用例场景中看起来很慢的原因,因此 Boost.Thread 与 STL 一样,在无竞争的用例中看起来比手动锁定代码慢得多。

  3. Boost.Thread 已经在用户模式下进行了大量工作,以避免在 Windows 上进入内核睡眠状态。在 POSIX 上,任何主要的 pthreads 实现也做了大量工作来避免内核休眠,因此 Boost.Thread 不会复制该工作。换句话说,关键部分在扩展加载行为方面并没有为您带来任何好处,尽管 Boost.Thread v4 不可避免地,尤其是在 Windows 上,做了大量的工作,而天真的实现却没有(计划中的 Boost.Thread 重写是巨大的)在 Windows 上更高效,因为它可以假定 Windows Vista 或更高版本。

【讨论】:

  • 建立自己的 IMO 是个坏建议。太容易出错了。有趣的部分是明确决定只使用一个互斥体类型来尝试完成所有三个场景,而不是允许程序员选择特定于他们的用例的类型。 (我可以看到支持这样做的论据,尽管我不确定这是我会做出的选择。)
  • 好吧,根据this 的说法,临界区 API 有一些优化,不会永远旋转等。不必重新发明这一切会很好。当然,在可移植的实现中,无论如何都必须重新发明它。
  • 此外,仅当您指定非零旋转计数时,它才会旋转。默认情况下它是never spins,但是一旦发生争用就会等待。
  • 我喜欢这个信息丰富的答案! (我想我会把它收藏起来作为参考)
【解决方案3】:

所以,看起来默认的 Boost 互斥体不支持它,但 asio::detail::mutex 支持。

所以我最终使用了它:

#include <boost/asio/detail/mutex.hpp>
#include <boost/thread.hpp>

using boost::asio::detail::mutex;
using boost::lock_guard;

int myFunc()
{
  static mutex mtx;
  lock_guard<mutex> lock(mtx);
  . . .
}

【讨论】:

  • Rustyx,这是无证的。它不是公共 API。使用风险自负。
猜你喜欢
  • 1970-01-01
  • 2016-08-02
  • 2017-12-03
  • 2011-07-08
  • 2012-11-13
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
  • 2017-01-03
相关资源
最近更新 更多