.NET EventWaitHandle 慢

Posted

技术标签:

【中文标题】.NET EventWaitHandle 慢【英文标题】:.NET EventWaitHandle slow 【发布时间】:2012-06-12 01:32:32 【问题描述】:

我正在使用带有回调函数的 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 范围内的值。

使用 .NET 或本机代码执行 OnWaveOut 回调内部底部循环中的部分而不是使用事件,使其始终保持快速。因此,问题似乎仅与 .NET 中的等待事件有关,并且主要仅在测试 PC 上发生“其他事情”时——但不是很多事情,可以像移动窗口或打开一样简单我电脑中的一个文件夹。

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

我还尝试使用接收事件而不是函数回调的 waveOutOpen 版本。这在 .NET 中也很慢,但在 C 中则不然,因此,它再次指出了事件和/或上下文切换的问题。

我试图让我的代码保持简单,并设置一个事件来完成回调之外的工作,这是我可以用我的整体设计做到这一点的最佳方式。其实只使用事件驱动的waveOut就更好了,但是我尝试了其他方法,因为直接回调很快,我没想到正常的事件等待句柄会这么慢。

【问题讨论】:

如果你不发布你的 .NET 代码,怎么会有人猜到它为什么会变慢?您需要在 plz-send-me-the-codez 问题上提升这一点。 很公平。猜猜我希望有人会说“这是一个已知问题,请改用 XYZ”。如果在我再次访问该代码时(明天某个时候)还没有答案,我会补充一点。 不确定我是否可以删除一个问题...我认为这个问题是 Win7、旧版 WinMM API 和 .NET 所特有的。最后,它为什么会发生并不重要,因为我将切换到 DirectSound,因为我很确定 Microsoft 的核心音频更改已经使波输入/输出不稳定(对于低延迟应用程序)。 只要我知道这一切都相反。 waveinout 仍然有效,directsound 已被弃用......无论如何。低延迟声音是一个挑战。我找到了一个租用编码器的人,他做了我需要的事情。自己动手就像把你@#$% 塞进卷笔刀里……不愉快 【参考方案1】:

也许不是 100% 相关,但我遇到了同样的问题:调用 EventWaitHandle.Set X 次很好,但是,在我无法提及的阈值之后,此方法的每次调用都需要 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)关于根据上下文选择如何同步的一些方法,性能方面稍微提了一下。

【讨论】:

以上是关于.NET EventWaitHandle 慢的主要内容,如果未能解决你的问题,请参考以下文章

等到 EventWaitHandle.Set() 之后通知所有进程

使用 EventWaitHandle 类

我需要 Dispose() 或 Close() EventWaitHandle 吗?

EventWaitHandle 没有在进程终止时关闭

EventWaitHandle

EventWaitHandle 第一课