Windows关键部分公平性

时间:2014-05-07 10:45:25

标签: c++ c windows multithreading critical-section

我使用EnterCriticalSection和LeaveCriticalSection方法对Windows上关键部分的公平性提出了质疑。 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);
  }
}

这样做的结果是一些线程被阻塞很多次,而另一些线​​程经常进入临界区。我也注意到如果你改变了更快的睡眠(10毫秒),一切都可能恢复公平,但我没有找到睡眠时间和公平之间的任何联系。 但是,这个测试示例比我真正的应用程序代码要好得多,后者要复杂得多,并且显示某些线程实际上是饥饿的。为了确保饥饿的线程还活着并且工作,我做了一个测试(在我的应用程序中),我在关键部分输入5次后杀死线程:结果是,最后,每个线程进入,所以我&#39 ;确定所有这些都在活塞上并且在互斥锁上被阻止。 我是否必须假设Windows对线程不公平? 你知道解决这个问题的方法吗?

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

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

2 个答案:

答案 0 :(得分:0)

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

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

例如,您可以重构代码以使单个线程处理长时间运行的操作,而其他线程将请求排队到该线程,阻塞完成事件。您只需在管理队列时短时间锁定关键部分。当然,如果这些操作也必须与其他操作互斥,那么您需要小心。如果所有这些东西都不能同时运行,那么你也可以通过队列序列化它。

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

答案 1 :(得分:0)

我认为你应该回顾一些事情:

  • 在10000个案例的9997中,您分支到Sleep(500)。几乎每次成功获得关键部分的尝试都会使每个线程保持最多500毫秒的政治部分。

  • 线程在释放临界区后执行另一个Sleep(500)。因此,通过持有关键部分,单个线程占用可用时间的近50%(49.985%) - 无论如何!

  • 幕后:Joe Duffy互斥锁的等待列表按FIFO顺序保存,操作系统总是唤醒此等待队列前面的线程。

假设您是故意这样做以显示行为:当处理器完全处理时,启动其中20个线程可能导致最后一个线程在单个逻辑处理器上访问关键部分的最短等待时间为10秒可用于此测试。

你需要多长时间才能进行测试/什么CPU?什么是Windows版本?您应该能够写下更多的事实:线程活动与线程ID的直方图可以说明公平性。

应在短时间内获得关键部分。在大多数情况下,可以更快地处理共享资源。关键部分内的Sleep几乎肯定会指出设计缺陷。

提示:减少在关键部分内花费的时间或调查Semaphore Objects