【问题标题】:C# Disable garbage collection for USB ReadPipeC# 禁用 USB ReadPipe 的垃圾收集
【发布时间】:2019-03-27 05:28:33
【问题描述】:

我正在尝试使用来自 FTDI 的 D3XX.NET 从 USB 端口收集数据。收集数据,然后将其发送到快速傅立叶变换以绘制光谱。即使您错过了一些数据,这也可以正常工作。你说不出来。但是,如果您想将此数据发送到音频输出组件,您会注意到数据丢失。这就是我的问题所在。 数据被收集,然后发送到音频设备。所有数据包都在所需的时间范围内完成。但是,音频正在丢弃它出现的数据。这是音频输出端正弦波的图片:

您可以看到开始时缺少一些数据,并且在接近尾声时似乎缺少整个周期。这只是一个例子,它一直在变化。有时似乎数据不存在。 我已经完成了整个处理链,我很确定声音的数据包正在制作它。 从那以后,我一直使用 JetBrains 性能分析器。我发现如下: ReadPipe 方法需要 8.5 毫秒,这正是您期望的读取时间。到目前为止,一切都很好。 ReadPipe 命令完成后,您有 0.5 毫秒的时间来执行另一个 ReadPipe,否则您将丢失一些数据。查看分析器输出,我看到了:

ReadPipe 需要 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();
        }
    }

等待句柄向其他线程表明数据可用。 那么GC是丢失数据的原因吗?如果是这样,我该如何避免这种情况? 谢谢!

【问题讨论】:

    标签: c# garbage-collection usb real-time


    【解决方案1】:

    如果您可以确认您没有耗尽内存,您可以尝试将GCSettings.LatencyMode 设置为GCLatencyMode.SustainedLowLatency。这将防止发生某些阻塞式垃圾回收,除非您的内存不足。查看docs on latency modes 了解更多详情和限制。

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

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

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

    【讨论】:

    • 您好,谢谢。我正在使用.Net 4.0。我尝试将 GC 更改为 lowLatency,还尝试了库提供的 ReadPipeAsync 方法,但没有任何区别。现在,D3XX.NET 库只是一堆非托管代码的 C# 包装器。那么将我的线程创建为非托管线程并使用其中的代码的想法是什么?
    • 是的,您必须使用一个函数创建自己的非托管库,该函数会生成一个后台线程来执行您需要的任何操作,然后 P/Invoke 进入其中。垃圾回收期间不会暂停非托管线程。不过,这相当复杂。
    • 我发布了关于如何执行此操作的后续文章
    【解决方案2】:

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

    简而言之,如果您的GC 正在停止您的线程,那么您就有了垃圾问题。 GC 将暂停所有线程以进行清理,除了更好地管理您创建的垃圾之外,您无能为力。

    如果您有数组,请不要不断地创建它们,而是重复使用它们(依此类推)。使用重量更轻的结构,使用可以减少分配的工具,例如 Span<T>Memory<T>。如果您的代码大量使用async,请考虑使用较少的awaits,并且不要将它们放入循环中。通过ref 并使用 ref locals 等,如果可以的话,也远离大型非托管数据块。

    此外,在没有关系的任何停机时间致电GC.Collect 可能会有所帮助,但更好的设计可能会更有益。

    【讨论】:

    • 代码中有几十万行。对于这种特定情况,这实际上是不可能的。那么,分析器是否表明该线程在 GC 期间阻塞?如果是,如何预防?
    • @tom 确实,它似乎是说垃圾收集阻塞了 150 毫秒,这在音频领域是很长的时间。您可以采取一些技巧来为垃圾收集提供提示,也许其他人可以在这里插话并提供一些建议。
    • 即89次调用的总时间为149ms。在 ReadPipe 发生的 4207 次中,它被调用了 89 次。每次调用它的时间为 1.4ms。这一定是故障的根源。
    • @Tom 查看此链接 stackoverflow.com/questions/9669963/… ,没有看到您的解决方案并且分析结果很难判断,但这可能会给 oyu 一个更好的指示,如果它是一个问题
    猜你喜欢
    • 2011-10-15
    • 2020-01-21
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多