简体   繁体   English

.NET EventWaitHandle 慢

[英].NET EventWaitHandle slow

I'm using waveOutWrite with a callback function, and under native code everything is fast.我正在使用具有回调函数的 waveOutWrite,在本机代码下一切都很快。 Under .NET it is much slower, to the point I think I'm doing something very wrong, 5 or 10 times slower sometimes.在 .NET 下它要慢得多,以至于我认为我做错了什么,有时慢 5 到 10 倍。

I can post both sets of code, but seems like too much, so I'll just post the C code that is fast and point out the minor variances in the .NET code.我可以发布两组代码,但似乎太多了,所以我只发布快速的 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;
      }
}

The times[] array for the C code always has values around 80, which is the PCM buffer length I am using. C 代码的 times[] 数组的值始终在 80 左右,这是我使用的 PCM 缓冲区长度。 The .NET code also shows similar values sometimes, however, it sometimes shows values as high as 1000, and more often values in the 300 to 500 range. .NET 代码有时也会显示类似的值,但有时会显示高达 1000 的值,更常见的是 300 到 500 范围内的值。

Doing the part that is in the bottom loop inside the OnWaveOut callback instead of using events, makes it fast all the time, with .NET or native code.在 OnWaveOut 回调中执行底部循环中的部分而不是使用事件,使用 .NET 或本机代码使其始终快速。 So it appears the issue is with the wait events in .NET only, and mostly only when "other stuff" is happening on the test PC -- but not a lot of stuff, can be as simple as moving a window around, or opening a folder in my computer.因此,问题似乎仅与 .NET 中的等待事件有关,并且主要仅在测试 PC 上发生“其他事情”时才发生 - 但不是很多事情,可以像移动窗口或打开一样简单我电脑里的一个文件夹。

Maybe .NET events are just really bad about context switching, or .NET apps/threads in general?也许 .NET 事件对上下文切换或 .NET 应用程序/线程来说真的很糟糕? In the app I'm using to test my .NET code, the code just runs in the constructor of a form (easy place to add test code), not on a thread-pool thread or anything.在我用来测试我的 .NET 代码的应用程序中,代码只是在表单的构造函数中运行(添加测试代码的容易位置),而不是在线程池线程或任何东西上。

I also tried using the version of waveOutOpen that takes an event instead of a function callback.我还尝试使用采用事件而不是函数回调的 waveOutOpen 版本。 This is also slow in .NET but not in C, so again, it points to an issue with events and/or context switching.这在 .NET 中也很慢,但在 C 中不是,所以再次指出事件和/或上下文切换的问题。

I'm trying to keep my code simple and setting an event to do the work outside the callback is the best way I can do this with my overall design.我试图保持我的代码简单,并设置一个事件来完成回调之外的工作是我可以用我的整体设计做到这一点的最佳方式。 Actually just using the event driven waveOut is even better, but I tried this other method because straight callbacks are fast, and I didn't expect normal event wait handles to be so slow.实际上只使用事件驱动的 waveOut 会更好,但我尝试了另一种方法,因为直接回调很快,而且我没想到正常的事件等待句柄会这么慢。

Maybe not 100% related but I faced somehow the same issue: calling EventWaitHandle.Set for X times is fine, but then, after a threshold that I can't mention, each call of this method takes 1 complete second!也许不是 100% 相关,但我遇到了同样的问题:调用EventWaitHandle.Set X 次很好,但是,在我无法提及的阈值之后,此方法的每次调用都需要 1 秒!

Is appears that some .net way to synchronize thread are much slower than the ones you use in C++.似乎某些 .net 同步线程的方法比您在 C++ 中使用的方法慢得多。

The all mighty @jonskeet once made a post on his web site ( https://jonskeet.uk/csharp/threads/waithandles.html ) where he also refers the very complex concept of .net synchronization domains explained here: https://www.drdobbs.com/windows/synchronization-domains/184405771强大的@jonskeet 曾经在他的网站 ( https://jonskeet.uk/csharp/threads/waithandles.html ) 上发帖,他还提到了非常复杂的 .net 同步域概念,解释如下: https:// www.drdobbs.com/windows/synchronization-domains/184405771

He mentions that .net and the OS must communicate in a very very very time precise way with object that must be converted from one environment to another.他提到 .net 和操作系统必须以非常非常非常精确的方式与必须从一种环境转换到另一种环境的对象进行通信。 All this is very time consuming.所有这些都非常耗时。

I summarized a lot here, not to take credit for the answer but there is an explanation.我在这里总结了很多,不是为了相信答案,而是有一个解释。 There are some recommendations here ( https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives ) about some ways to choose how to synchronize depending on the context, and the performance aspect is mentioned a little bit.这里有一些建议( https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives )关于根据上下文和性能选择同步方式的一些方法方面略有提及。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM