代码之家  ›  专栏  ›  技术社区  ›  Tom

禁用USB读管的垃圾收集

  •  4
  • Tom  · 技术社区  · 7 年前

    我正在尝试使用ftdi的d3xx.net从USB端口收集数据。收集数据,然后发送到一个快速傅立叶变换绘制频谱。即使您错过了一些数据,这也可以正常工作。你看不出来。但是,如果您希望将此数据发送到音频输出组件,则会注意到数据丢失。这就是我的问题所在。 数据被收集,然后发送到音频设备。所有数据包都在所需的时间范围内完成。但是,音频正在删除显示的数据。这是一张正弦波在音频输出时的样子的图片:

    您可以看到一些数据在开始时丢失,而在接近结束时似乎整个周期都丢失了。这只是一个例子,它一直在变化。有时数据似乎不在那里。 我已经完成了整个处理链,我很确定声音的数据包正在生成。 从那以后我就用了JetBrains性能分析器。我发现如下:readpipe方法需要8.5毫秒,这正是您希望读取所需要的。到现在为止,一直都还不错。一旦readpipe命令完成,您将有0.5毫秒的时间来执行另一个readpipe,否则将丢失一些数据。查看探查器输出,我看到:

    readpipe需要8.5毫秒,然后有一个用于垃圾收集的条目,平均需要1.6毫秒。如果确实偶尔发生这种情况,那么我丢失了一些数据。

    下面是代码:它是一个后台工作人员:

    private void collectdata(object sender,doWorkEventargs e)
    {
    同时(保持)
    {
    ftstatus=d32xdevice.readpipe(0x84,iqbuffer,65536,ref bytestransferred);//读取iq数据-将得到1024对-每个值2字节
    
    _ waitForData.set();
    }
    }
    < /代码> 
    
    

    waithandle向另一个线程表示数据可用。 那么GC是导致数据丢失的原因吗?如果是这样,我怎么能避免呢? 谢谢!

    enter image description here

    您可以看到一些数据在开始时丢失,而在接近结束时似乎整个周期都丢失了。这只是一个例子,它一直在变化。有时数据似乎不在那里。 我已经完成了整个处理链,我很确定声音的数据包正在生成。 从那以后我就用了JetBrains性能分析器。我发现如下:readpipe方法需要8.5毫秒,这正是您希望读取所需要的。到现在为止,一直都还不错。一旦readpipe命令完成,您将有0.5毫秒的时间来执行另一个readpipe,否则将丢失一些数据。查看探查器输出,我看到:

    enter image description here

    读取管道需要8.5毫秒,然后有一个垃圾收集条目,平均需要1.6毫秒。如果确实偶尔发生这种情况,那么我就丢失了一些数据。

    下面是代码:它是一个后台工作人员:

        private void CollectData(object sender, DoWorkEventArgs e)
        {
            while (keepGoing)
            {
                ftStatus = d3xxDevice.ReadPipe(0x84, iqBuffer, 65536, ref bytesTransferred); //read IQ data - will get 1024 pairs - 2 bytes per value
    
                _waitForData.Set();
            }
        }
    

    waithandle向另一个线程表示数据可用。 那么GC是导致数据丢失的原因吗?如果是这样,我怎么能避免呢? 谢谢!

    2 回复  |  直到 7 年前
        1
  •  3
  •   Collin Dauphinee    7 年前

    如果可以确认内存没有用完,可以尝试设置 GCSettings.LatencyMode GCLatencyMode.SustainedLowLatency . 这将防止发生某些阻塞垃圾收集,除非内存不足。退房 docs on latency modes 了解更多详细信息和限制。

    如果垃圾收集对您的用例来说仍然太具有破坏性,并且您使用的是.NET 4.6或更高版本,则可以尝试调用 GC.TryStartNoGCRegion . 此方法将尝试保留足够的内存以分配到指定的数量,并阻止GC,直到您用完保留为止。如果您的内存使用相当一致,那么您可能可以通过传递足够大的值来适应应用程序的使用,但不能保证调用会成功。

    如果你使用的是旧版本的.NET,它不支持这两种功能,那么你可能走运了。如果这是一个GUI应用程序(从事件处理程序判断,它看起来像),那么您对分配没有足够的控制。

    另一个需要考虑的问题是,对于不能容忍中断的应用程序来说,C并不是真正合适的工具。如果您熟悉编写本机代码,则可以在非托管线程上执行时间敏感的工作;据我所知,这是唯一可靠的解决方案,尤其是当您的应用程序将在最终用户计算机上运行时。

        2
  •  2
  •   TheGeneral    7 年前

    你需要对你的垃圾收集器更友好,不要分配太多。

    简而言之,如果你的 GC 正在暂停线程,您有一个垃圾问题。GC将暂停所有线程来进行清理,除了更好地管理所创建的垃圾之外,没有什么可以真正做的。

    如果您有数组,不要不断地创建它们,而是重用它们(等等)。使用重量更轻的结构,使用允许您减少分配的工具,例如 Span<T> Memory<T> . 考虑少用 awaits 如果您的代码很重 async 不要把它们放在循环中。路过 ref 并使用ref局部变量等,如果可以的话,也要远离大型非托管数据块。

    另外,打电话可能会有好处 GC.Collect 在任何不重要的停机时间,尽管更好的设计可能会更有益。