C# 禁用 USB ReadPipe 的垃圾回收

C# Disable garbage collection for USB ReadPipe

我正在尝试使用来自 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是数据丢失的原因吗?如果是这样,我该如何避免这种情况? 谢谢!

你需要对你的垃圾收集器友好一点,不要分配那么多。

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

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

此外,在无关紧要的任何停机时间调用 GC.Collect 可能会有所帮助,但更好的设计可能会更有益。

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

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

如果您使用的是不支持其中任何一个的旧版 .NET,您可能就不走运了。如果这是一个 GUI 应用程序(从事件处理程序来看它看起来像),则您对分配没有足够的控制权。

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