我有一种情况,我需要在UI线程上一个接一个地处理大量文件,每个文件的加载和处理都可能需要大量的时间,并且它们一起使用太多的内存,一次加载太多.这些都是在.NET4.8应用程序中运行的,不幸的是,在同步的代码库的一部分中运行.

从本质上讲,我的代码如下所示:

IEnumerable<ScanFile> stream = GetScanFileStream();

foreach (ScanFile scanFile in scanFiles)
{
    Process(scanFile); // Requires to be called on the UI thread
}

IEnumerable<ScanFile> GetScanFileStream() =>
    from filePath in Directory.EnumerateFiles("c:\\scans", "*.json")
    select this.LoadScanFile(filePath); // I like to run this in the background
}

执行LoadScanFile的时间大致相当于执行Process的时间,因此,我希望通过在操作仍在UI线程上运行的同时在后台线程上预加载下一个文件,将处理这些文件所需的时间减少一半.

我试图创建一个特殊的IEnumerable<T>修饰符实现,包装允许这种行为的原始流,但我很快发现实现变得太复杂了.我开始使用信号量在线程之间进行同步.就在那时,我不再追求这个解决方案,认为应该有更简单的解决方案,具有同样的效果.

我本以为可以通过使用LINQ来并行使用BCL和CLR中内置的构造来实现这种行为,但在谷歌上进行广泛搜索并没有得到任何好的结果.

您有什么建议的解决方案可以让我将处理时间减半?

推荐答案

您可以使用IAsyncEnumerable,并使LoadScanFile为异步(使用异步函数或使用Task.Run).然后在放弃前一个任务之前调用下一个任务.

IAsyncEnumerable<ScanFile> GetScanFileStream()
{
    ScanFile scanFile = null;
    foreach (var filePath in Directory.EnumerateFiles("c:\\scans", "*.json"))
    {
        // start the next task
        var scanFileTask = this.LoadScanFileAync(filePath);
        // if we have one already yield it
        if (scanFile != null)
            yield return scanFile;

        scanFile = await scanFileTask;
    }
    if (scanFile != null)    // and yield the last one also
        yield return scanFile;
}

现在你可以做

await foreach (ScanFile scanFile in scanFiles)
{
    Process(scanFile); // Requires to be called on the UI thread
}

.net相关问答推荐

如何在PowerShell中隐藏任务延迟输出?

cmd 冻结中的 dotnet 命令.怎么了?

.NET 中两个子字符串之间的非贪婪正则表达式匹配

将 Span 传递到函数时出现 F# 错误

将 int 转换为 .NET 中的位数组

MongoDB C# 驱动程序 - 忽略绑定字段

不同命名空间中的部分类

来自奥尔森时区的 .NET TimeZoneInfo

无法加载文件或程序集WebGrease,版本=1.5.1.25624,Culture=neutral,PublicKeyToken=31bf3856ad364e35或其依赖项之一

C# 中 try/finally 的开销?

将跟踪输出重定向到控制台

C#:获得完整的桌面大小?

如何在 C# 中以编程方式安装 Windows 服务?

在 C#/.NET 中组合路径和文件名的最佳方法是什么?

.NET 的黄瓜替代品

如何正确和完全关闭/重置 TcpClient 连接?

在 .NET 中获取默认打印机的最佳方法是什么

SqlCommand.CommandTimeout 和 SqlConnection.ConnectionTimeout 有什么区别?

程序员应该使用 SSIS,如果是,为什么?

在 .NET 中乘以时间跨度