【问题标题】:Windows Critical Section - how to disable spinning completelyWindows 关键部分 - 如何完全禁用旋转
【发布时间】:2019-07-12 21:35:36
【问题描述】:

我正在尝试通过不同的方法将 CRITICAL_SECTION 的旋转计数设置为零:

int main()
{
    CRITICAL_SECTION cs;

    ::InitializeCriticalSection(&cs);
    printf("Spin count by default %08X\n", cs.SpinCount);
    ::DeleteCriticalSection(&cs);

    ::InitializeCriticalSectionAndSpinCount(&cs, 0);
    printf("Spin count with zero spin count init %08X\n", cs.SpinCount );
    ::DeleteCriticalSection(&cs);


    ::InitializeCriticalSectionEx(&cs, 0, 0);
    printf("Spin count with zero spin count and flags init %08X\n", cs.SpinCount );
    ::DeleteCriticalSection(&cs);

    ::InitializeCriticalSection(&cs);
    ::SetCriticalSectionSpinCount(&cs, 0);
    printf("Spin count after explicit reset to zero %08X\n", cs.SpinCount);
    ::DeleteCriticalSection(&cs);
}

在 Windows 7 中,所有结果都如预期的那样为 0。

在 Windows 10 中,除了最后一个,所有结果都是 0x020007D0 值。最后一个结果是0x02000000

显然,0x07D0 是实际的旋转计数(2000 十进制),0x02000000 是这些标志之一:

#define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO         0x01000000
#define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN          0x02000000
#define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT           0x04000000
#define RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE         0x08000000
#define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO      0x10000000

我担心RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 可能会导致临界区旋转,即使我使用SetCriticalSectionSpinCount 要求它不要旋转。

有没有办法不使用标准文档 API 来定义 RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN

【问题讨论】:

  • 听起来 API 可能会强制执行最小旋转计数。为什么你不希望它旋转?纺纱很好。
  • 我知道特定的操作很慢,所以如果拥有关键部分,那么烧资源旋转没有意义,它不太可能很快免费,而是让给另一个线程会有所帮助这样资源可能会更快地释放出来。
  • 它似乎没有完全强制执行最小值,因为将旋转计数设置为 1 确实将其设置为 1。调用 SetCriticalSectionSpinCount 也可以 - 它可以将旋转计数设置为零。我唯一的问题显然是强制执行RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 标志。
  • 如果您不想阻止,总有TryEnterCriticalSection()。或者你可以使用互斥锁。请注意,RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 未记录在案;似乎没有任何关于它的实际用途的信息。
  • TryEnterCriticalSection 不是一个选项,因为如果拥有对象,我想阻止。 WinAPI 互斥锁也不是一个选项,因为现在拥有对象时我不想进入内核。最接近的替代方案是 Slim Reader/Writer Lock,当被AcquireSRWLockExclusive/ReleaseSRWLockExclusive 锁定时,它似乎完全符合我的需要,只是它没有递归支持。在 C++ 中,boost::mutexstd::mutex 的 Visual Studio 实现也可以按照我的意愿行事(甚至还有这些的递归版本)。然而,我的问题是关于控制关键部分的行为。

标签: windows multithreading winapi critical-section


【解决方案1】:

窥探实现后,自己想出了答案。

InitializeCriticalSectionEx 与非零旋转计数一起使用时,RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN 标志未设置。所以这段代码输出00000001:

::InitializeCriticalSectionEx(&cs, 1, 0);
printf("Spin count after explicit one %08X\n", cs.SpinCount);
::DeleteCriticalSection(&cs);

一个旋转计数几乎为零旋转计数。此外,之后调用SetCriticalSectionSpinCount 可以将其重置为零。所以这段代码输出00000000:

::InitializeCriticalSectionEx(&cs, 1, 0);
::SetCriticalSectionSpinCount(&cs, 0);
printf("Spin count after explicit one and then reset to zero %08X\n", cs.SpinCount);
::DeleteCriticalSection(&cs);

当然,禁用旋转应该有令人信服的理由。默认情况下,正如@JonathanPotter 指出的那样,旋转很好。否则它不会被设置为默认行为。所以我什至没有将禁用旋转的解决方案应用于我原来的问题。

另一方面,关键部分的维护者可能无意不尊重传递给InitializeCriticalSectionExInitializeCriticalSectionAndSpinCount 的零自旋计数。他们只是确保普通的InitializeCriticalSection 获得自动旋转计数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 2023-01-12
    相关资源
    最近更新 更多