.NET EventWaitHandle很慢

时间:2012-06-07 23:27:28

标签: c# .net multithreading events thread-synchronization

我正在使用带有回调函数的waveOutWrite,在本机代码下,一切都很快。在.NET下它速度要慢得多,我觉得我做错了,有时慢了5到10倍。

我可以发布两组代码,但看起来太多了,所以我只是发布快速的C代码并指出.NET代码中的微小差异。

HANDLE WaveEvent;
const int TestCount = 100;
HWAVEOUT hWaveOut[1]; // don't ask why this is an array, just test code
WAVEHDR woh[1][20];

void CALLBACK OnWaveOut(HWAVEOUT,UINT uMsg,DWORD,DWORD,DWORD)
{
   if(uMsg != WOM_DONE)
      return;
   assert(SetEvent(WaveEvent)); // .NET code uses EventWaitHandle.Set()
}

void test(void)
{
   WaveEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
   assert(WaveEvent);

   WAVEFORMATEX wf;
   memset(&wf,0,sizeof(wf));
   wf.wFormatTag =  WAVE_FORMAT_PCM;
   wf.nChannels = 1;
   wf.nSamplesPerSec = 8000;
   wf.wBitsPerSample = 16;
   wf.nBlockAlign = WORD(wf.nChannels*(wf.wBitsPerSample/8));
   wf.nAvgBytesPerSec = (wf.wBitsPerSample/8)*wf.nSamplesPerSec;

   assert(waveOutOpen(&hWaveOut[0],WAVE_MAPPER,&wf,(DWORD)OnWaveOut,0,CALLBACK_FUNCTION) == MMSYSERR_NOERROR);

   for(int x=0;x<2;x++)
      {
      memset(&woh[0][x],0,sizeof(woh[0][x]));
      woh[0][x].dwBufferLength = PCM_BUF_LEN;
      woh[0][x].lpData = (char*) malloc(woh[0][x].dwBufferLength);
      assert(waveOutPrepareHeader(hWaveOut[0],&woh[0][x],sizeof(woh[0][x])) == MMSYSERR_NOERROR);
      assert(waveOutWrite(hWaveOut[0],&woh[0][x],sizeof(woh[0][x])) == MMSYSERR_NOERROR);
      }

   int bufferIndex = 0;
   DWORD times[TestCount];
   for(int x=0;x<TestCount;x++)
      {
      DWORD t = timeGetTime();
      assert(WaitForSingleObject(WaveEvent,INFINITE) == WAIT_OBJECT_0); // .NET code uses EventWaitHandle.WaitOne()
      assert(woh[0][bufferIndex].dwFlags & WHDR_DONE);
      assert(waveOutWrite(hWaveOut[0],&woh[0][bufferIndex],sizeof(woh[0][bufferIndex])) == MMSYSERR_NOERROR);
      bufferIndex = bufferIndex == 0 ? 1 : 0;
      times[x] = timeGetTime() - t;
      }
}

C代码的times []数组总是有大约80的值,这是我正在使用的PCM缓冲区长度。 .NET代码有时也会显示类似的值,但有时会显示高达1000的值,更常见的值是300到500范围内的值。

在OnWaveOut回调中执行底部循环中的部分而不是使用事件,使用.NET或本机代码使其一直很快。因此,问题似乎只出现在.NET中的等待事件,并且大多数情况下只有在测试PC上发生“其他东西”时 - 而不是很多东西,可以像移动窗口一样简单,或者打开我电脑里的一个文件夹。

也许.NET事件对于上下文切换,或者一般的.NET应用程序/线程来说真的很糟糕?在我用来测试我的.NET代码的应用程序中,代码只是在表单的构造函数(添加测试代码的简单位置)中运行,而不是在线程池线程或任何东西上运行。

我也尝试使用waveOutOpen的版本,它接受事件而不是函数回调。这在.NET中也很慢,但在C中也没有,所以再次指出事件和/或上下文切换的问题。

我正在尝试保持我的代码简单,并设置一个事件来做回调之外的工作是我用我的整体设计做到这一点的最好方法。实际上只是使用事件驱动的waveOut甚至更好,但我尝试了另一种方法,因为直接回调很快,我没想到正常的事件等待句柄会这么慢。

1 个答案:

答案 0 :(得分:0)

也许不是 100% 相关,但我遇到了同样的问题:X 次调用 EventWaitHandle.Set 很好,但是,在我无法提及的阈值之后,此方法的每次调用都需要 1 秒!

似乎某些 .net 同步线程的方法比您在 C++ 中使用的方法慢得多。

强大的@jonskeet 曾经在他的网站 (https://jonskeet.uk/csharp/threads/waithandles.html) 上发表过一篇文章,他还提到了非常复杂的 .net 同步域概念,解释如下:https://www.drdobbs.com/windows/synchronization-domains/184405771

他提到 .net 和操作系统必须以非常非常非常精确的方式与必须从一种环境转换到另一种环境的对象进行通信。所有这些都非常耗时。

我在这里总结了很多,不是为了相信答案,而是有一个解释。这里有一些建议 (https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives) 关于根据上下文选择如何同步的一些方法,并且稍微提到了性能方面。