c++ - Windows 临界区公平性

标签 c++ c windows multithreading critical-section

我对 Windows 上使用 EnterCriticalSection 和 LeaveCriticalSection 方法的关键部分的公平性有疑问。 MSDN 文档指出:“无法保证线程获得临界区所有权的顺序,但是系统对所有线程都是公平的。” 问题出在我编写的一个应用程序上,它阻塞了一些永远不会进入临界区的线程,即使在很长一段时间后也是如此;所以我用一个简单的 C 程序执行了一些测试,以验证这种行为,但是当你有很多线程并且内部有一些等待时间时,我注意到奇怪的结果。 这是测试程序的代码:

CRITICAL_SECTION CriticalSection;

DWORD WINAPI ThreadFunc(void* data) {
  int me;
  int i,c = 0;;
  me = *(int *) data;
  printf(" %d started\n",me);
  for (i=0; i < 10000; i++) {
     EnterCriticalSection(&CriticalSection);
     printf(" %d Trying to connect (%d)\n",me,c);
     if(i!=3 && i!=4 && i!=5)
         Sleep(500);
     else
         Sleep(10);
    LeaveCriticalSection(&CriticalSection);
     c++;
     Sleep(500);
  }
  return 0;
}

int main() {
  int i;
  int a[20];
  HANDLE thread[20];

  InitializeCriticalSection(&CriticalSection);
  for (i=0; i<20; i++) {
        a[i] = i;
        thread[i] = CreateThread(NULL, 0, ThreadFunc, (LPVOID) &a[i], 0, NULL);
  }
}

这样做的结果是,有些线程被阻塞了很多周期,而另一些线​​程则经常进入临界区。我还注意到,如果你改变更快的 sleep (10 毫秒),一切可能都会恢复公平,但我没有发现 sleep 时间和公平之间有任何联系。 然而,这个测试示例比我的真实应用程序代码要好得多,后者要复杂得多,并且实际上显示了某些线程的饥饿情况。为了确保饥饿的线程处于事件状态并正常工作,我做了一个测试(在我的应用程序中),在关键部分中输入 5 次后我杀死线程:结果是,最后,每个线程都进入,所以我确保它们全部都处于事件状态并且被互斥锁阻塞。 我是否必须假设 Windows 对于线程确实不公平? 您知道这个问题有什么解决办法吗?

编辑:Linux 中使用 pthreads 的相同代码,按预期工作(没有线程匮乏)。

EDIT2:我找到了一个可行的解决方案,使用 CONDITION_VARIABLE 强制公平。 可以从这篇文章( link )中推断出来,并进行必要的修改。

最佳答案

无论如何,你都会在这里遇到饥饿问题,因为关键部分被保留了很长时间。
我认为 MSDN 可能建议调度程序在唤醒线程方面是公平的,但由于没有锁获取顺序,因此它实际上可能并不像您期望的那样“公平”。 您是否尝试过使用互斥体而不是关键部分?另外,您是否尝试过调整旋转计数?

如果您可以避免长时间锁定关键部分,那么这可能是处理此问题的更好方法。

例如,您可以重构代码,让一个线程处理长时间运行的操作,其他线程将对该线程的请求排队,并在完成事件上阻塞。管理队列时只需要短时间锁定临界区即可。当然,如果这些操作还必须与其他操作互斥,那么您需要小心。如果所有这些东西不能同时操作,那么您也可以通过队列对其进行序列化。

或者,也许可以看看使用 boost asio。您可以使用线程池和链来防止多个异步处理程序同时运行,否则同步会成为问题。

关于c++ - Windows 临界区公平性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23515630/

相关文章:

c - 在数组表示和除数中查找数字的模数

windows - Jenkins 如何使用 Windows 批处理命令使用环境变量?

c++ - 无法在 visual studio 2008 中包含 std 头文件

c++ - 'managed_shared_memory' 应该分配多少内存? ( boost )

c++ - 编译优化问题

c++ - 以下程序的格式说明符可能是什么?

c - 在 C 中通过 argv 使用管道发送和接收字符数组

windows - 如何使用 Delphi 查找 Windows 用户(不是当前用户)的配置文件目录?

c++ - sigc++ 与 lambda 给出错误 : void value not ignored as it ought to be

c++ - 与 double 混合时使用 int 和 unsigned int 之间的速度差异