Boost 是否支持 Windows EnterCriticalSection API?
Does Boost have support for Windows EnterCriticalSection API?
我知道 Boost 支持互斥和 lock_guard,可用于实现临界区。
但是 Windows 有一个特殊的 API 关键部分(参见 EnterCriticalSection and LeaveCriticalSection),它比互斥锁快很多(对于很少竞争的短代码部分)。
因此我的问题 - 可以在 Boost 中利用这个 API,并回退到 spinlock/mutex/futex-based 在其他平台上实施?
简单的答案是否定的。
这里有一些相关的背景知识from an old mailing list thread:
BTW. I am agree that mutex is more universal solution from a
performance point of view. But to be fair - CS are faster in simple
design. I believe that possibility to support them should be at
least
taken in account.
这是有人指给我看的文章。结论是
只有在以下情况下,CS 才会更快:
- 进程中的线程总数少于 8 个。
- 你不在后台 运行。
- 您使用的不是双处理器计算机。
对我来说,这意味着简单的测试可以产生良好的 CS 性能
结果,但是任何真实世界的程序都最好有一个完整的
互斥。
我并不反对支持 CS 实现。但是,我
最初选择不这样做的原因如下:
- 你可以通过使用 PIMPL 获得构建和破坏命中
成语或者你必须在 Boost.Threads headers 中包含 Windows.h,
我根本不想这样做。 (这可以解决
从 MSDN 模拟 CS ala OPTEX。)
- 根据这篇研究论文,大多数 程序不会受益于
CS 设计。
- 编写一个 (non-portable) critical_section class 很简单
如果您真的可以利用它,请遵循 Mutex 模型。
现在我认为我做出了正确的选择,尽管在未来的道路上我们
可能会更改实现以使用临界区或 OPTEX。
比尔·肯普夫
作为帮助维护 Boost.Thread 的人以及未能将事件对象放入 Boost.Thread 的人,我认为关键部分从未被添加过,也不会被添加出于以下原因提升:
使用 boost::atomic 和 boost::condition_variable 构建 Win32 临界区非常容易,以至于不值得拥有一个官方的。这可能是您能想象到的最复杂的一个,但可配置性极强,包括准备好 constexpr(不要问!):https://github.com/ned14/boost.outcome/blob/master/include/boost/outcome/v1/spinlock.hpp#L331
您可以简单地通过匹配(基本)可锁定概念并使用原子 compare_exchange(non-x86/x64)或原子交换(x86/x64)来构建您自己的,然后使用 lock_guard临界区附近。
有人可能会反对win32临界区不是这个。恐怕是:它只是在一个原子上自旋一个自旋计数,然后懒洋洋地尝试分配一个 win32 事件对象,然后等待它。没什么特别的。
尽管您可能认为临界区(真正的用户模式互斥体)是 better/faster/whatever,但它们可能并不像您想象的那么好。 boost::mutex 是 Windows 内部使用 win32 信号量作为内核等待对象的巨大重量级事物,因为需要模拟线程取消并表现良好通用用途上下文。 很容易 编写一个对于某些单一用例来说比另一个更快的并发结构,但是编写一个包含以下所有内容的并发结构非常非常困难:
- 在无竞争情况下比标准实施更快。
- 在竞争激烈的情况下比标准实施更快。
- 在竞争激烈的情况下比标准实施更快。
即使您管理了以上所有三项,这仍然不够:您还需要对最坏情况的进展顺序进行一些保证,以便确定锁定、等待和解锁的某些模式是否会产生可预测的结果。这就是为什么线程设施在狭窄的用例场景中看起来很慢,所以 Boost.Thread 就像在没有竞争的用例中,STL 看起来比手动锁定代码慢得多。
Boost.Thread 已经在用户模式下做了大量工作以避免在 Windows 上进入内核睡眠。在 POSIX 上,任何主要的 pthreads 实现也做大量工作来避免内核休眠,因此 Boost.Thread 不会复制该工作。换句话说,关键部分在扩展负载行为方面不会给你带来任何好处,尽管不可避免地 Boost.Thread v4 特别是在 Windows 上做了大量的工作,而天真的实现不会(计划的重写Boost.Thread 在 Windows 上的效率要高得多,因为它可以假定 Windows Vista 或更高版本)。
因此,看起来默认的 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);
. . .
}
我知道 Boost 支持互斥和 lock_guard,可用于实现临界区。
但是 Windows 有一个特殊的 API 关键部分(参见 EnterCriticalSection and LeaveCriticalSection),它比互斥锁快很多(对于很少竞争的短代码部分)。
因此我的问题 - 可以在 Boost 中利用这个 API,并回退到 spinlock/mutex/futex-based 在其他平台上实施?
简单的答案是否定的。
这里有一些相关的背景知识from an old mailing list thread:
BTW. I am agree that mutex is more universal solution from a
performance point of view. But to be fair - CS are faster in simple design. I believe that possibility to support them should be at least taken in account.
这是有人指给我看的文章。结论是 只有在以下情况下,CS 才会更快:
- 进程中的线程总数少于 8 个。
- 你不在后台 运行。
- 您使用的不是双处理器计算机。
对我来说,这意味着简单的测试可以产生良好的 CS 性能 结果,但是任何真实世界的程序都最好有一个完整的 互斥。
我并不反对支持 CS 实现。但是,我 最初选择不这样做的原因如下:
- 你可以通过使用 PIMPL 获得构建和破坏命中 成语或者你必须在 Boost.Threads headers 中包含 Windows.h, 我根本不想这样做。 (这可以解决 从 MSDN 模拟 CS ala OPTEX。)
- 根据这篇研究论文,大多数 程序不会受益于 CS 设计。
- 编写一个 (non-portable) critical_section class 很简单 如果您真的可以利用它,请遵循 Mutex 模型。
现在我认为我做出了正确的选择,尽管在未来的道路上我们 可能会更改实现以使用临界区或 OPTEX。
比尔·肯普夫
作为帮助维护 Boost.Thread 的人以及未能将事件对象放入 Boost.Thread 的人,我认为关键部分从未被添加过,也不会被添加出于以下原因提升:
使用 boost::atomic 和 boost::condition_variable 构建 Win32 临界区非常容易,以至于不值得拥有一个官方的。这可能是您能想象到的最复杂的一个,但可配置性极强,包括准备好 constexpr(不要问!):https://github.com/ned14/boost.outcome/blob/master/include/boost/outcome/v1/spinlock.hpp#L331
您可以简单地通过匹配(基本)可锁定概念并使用原子 compare_exchange(non-x86/x64)或原子交换(x86/x64)来构建您自己的,然后使用 lock_guard临界区附近。
有人可能会反对win32临界区不是这个。恐怕是:它只是在一个原子上自旋一个自旋计数,然后懒洋洋地尝试分配一个 win32 事件对象,然后等待它。没什么特别的。
尽管您可能认为临界区(真正的用户模式互斥体)是 better/faster/whatever,但它们可能并不像您想象的那么好。 boost::mutex 是 Windows 内部使用 win32 信号量作为内核等待对象的巨大重量级事物,因为需要模拟线程取消并表现良好通用用途上下文。 很容易 编写一个对于某些单一用例来说比另一个更快的并发结构,但是编写一个包含以下所有内容的并发结构非常非常困难:
- 在无竞争情况下比标准实施更快。
- 在竞争激烈的情况下比标准实施更快。
- 在竞争激烈的情况下比标准实施更快。
即使您管理了以上所有三项,这仍然不够:您还需要对最坏情况的进展顺序进行一些保证,以便确定锁定、等待和解锁的某些模式是否会产生可预测的结果。这就是为什么线程设施在狭窄的用例场景中看起来很慢,所以 Boost.Thread 就像在没有竞争的用例中,STL 看起来比手动锁定代码慢得多。
Boost.Thread 已经在用户模式下做了大量工作以避免在 Windows 上进入内核睡眠。在 POSIX 上,任何主要的 pthreads 实现也做大量工作来避免内核休眠,因此 Boost.Thread 不会复制该工作。换句话说,关键部分在扩展负载行为方面不会给你带来任何好处,尽管不可避免地 Boost.Thread v4 特别是在 Windows 上做了大量的工作,而天真的实现不会(计划的重写Boost.Thread 在 Windows 上的效率要高得多,因为它可以假定 Windows Vista 或更高版本)。
因此,看起来默认的 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);
. . .
}