c++ - EnterSynchronizationBarrier卡在Windows 8中

标签 c++ multithreading winapi windows-8

我试图将新的API用于Windows 8的同步障碍,但以下简单代码有时会在Windows 8中挂起:

#undef WINVER
#define WINVER 0x0603
#include "windows.h"
#include <thread>
#include <vector>

int main()
{
  SYNCHRONIZATION_BARRIER barrier;
  int count = 32;
  InitializeSynchronizationBarrier (&barrier, count, -1);
  std::vector<std::thread> threads;

  for (int thr_num = 0; thr_num < count; thr_num++)
  {
    threads.emplace_back ([thr_num]
    {
      for (int i = 0; i < 100000; i++)
        EnterSynchronizationBarrier (&barrier, 0);
    });
  }

  for (auto &thr : threads)
    thr.join ();

  return 0;
}

在32核双Xeon E5 2630的Windows 8.1 64位上进行了测试。在十次启动中,它大约挂起一次。
似乎在Windows 10中它可以正常工作(在另一台计算机上)。这是已修复的Windows 8中的错误,还是EnterSynchronizationBarrier的用法不正确(也许您不能在循环中调用它?)。关于此功能的信息不多,有人使用过吗?

最佳答案

几年后并没有什么关系,除了可能表明某些问题太过晦涩难懂,以至于Stack Overflow无法在有用的时间内给予密切关注,但您的用法是正确的(如果极端的话),并且您对被调用函数施加的压力确实会引起注意。已经暴露出内存障碍的问题。
在您的片段中,为32个线程准备了一个同步屏障,您创建了32个线程,每个线程都进入100000个同步工作阶段。所有32个电话都将其调用号码N到达EnterSynchronizationBarrier,然后全部释放到其调用号码N + 1的途中。它应该工作。如果您的阶段有任何实质内容,则可能会发生。
需要强调的是,调用之间的每个阶段只是循环返回以重复调用的过程中所涉及的指令很少。当结束阶段N的最后一个线程处于调用中时,它发出信号通知其他线程离开,并且他们有很大的机会离开(甚至重新进入该函数以结束其阶段N + 1),而结束阶段N的线程仍在进行内部记账。
在这种簿记中有两个柜台。当线程进入同步屏障时,根据Microsoft的符号文件命名的屏障会减少。另一个名为LeftBarrier,随着离开它们而增加。结束阶段的线程从LeftBarrier重置Barrier(应该是所有参与线程的计数),并将LeftBarrier重置为1。这样可以简化。
复杂的现实是,屏障计数过载:其高位表示相位变化。如果等待同步屏障的线程正在旋转而不是在事件上阻塞,则在旋转时检查的对象是屏障中的高位是否已更改。因此,真正重要的是计数器如何在结束线程的簿记中重置。顺序为:读取LeftBarrier;将LeftBarrier写为1;将Barrier写为旧的LeftBarrier,将高位切换。
我认为发生的事情是,没有内存屏障,可以在LeftBarrier之前写入Barrier计数,但是由于Barrier的高位已切换,因此旋转线程脱离了自旋,并在第一个将其重置为1之前从另一个处理器递增LeftBarrier 。增量丢失,在此之后所有投注都被取消,因为后续阶段将发现阶段结束时的LeftBarrier不再是参与线程的计数。
Windows 8和8.1在这里没有内存障碍。 Windows 10确实可以,但是我认为它的位置不正确,并且Windows Vista和Windows 7在两次写入之间可以正确地进行安装。无论如何,该实现已针对1607版进行了完全重新设计,因此它现在使用了WaitOnAddress功能,这与后来的Raymond Chen博客(而不是您的一位记者引用的博客)所描绘的大致相同。在引用博客时,微软(尽管可能不是雷蒙德)肯定知道该函数的两个较早的代码更改(关于内存障碍)。

关于c++ - EnterSynchronizationBarrier卡在Windows 8中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37378214/

相关文章:

c++ - 使用 MinGW 让 SDL2 在 Clion、Windows 10 上运行

.net - 如何在ASP.NET MVC应用程序中的线程上放置代码?

Java:如何让已完成的线程从正在运行的线程中获取任务

c++ - 递归函数中的堆栈溢出

c++ - 用数学表达式拆分字符串

c++ - 是否删除 std unordered_map 的元素使用的已删除堆内存

具有数组类型的 C++ add_pointer 模板

c++ - QThread::wait() 和 QThread::finished()

关于系统托盘最小化 - WIN API 的几个问题

c++ - 使用 SPI_SETMOUSESPEED 不改变鼠标速度