C# .NET Core 3.1 代码与 Visual Studio 长期执行一样好,但发布的单个文件可执行文件会消耗大



我想发布我的应用程序,因为它运行良好。但是当我将其作为打包的应用程序运行时,它使用了高达 20 GB 甚至更多的大量内存。在Visual Studio 2019中运行它,我无法重现该问题。它的运行时间也很长,在VS中即使有很多文件也需要几秒钟。

我尝试了我认为到目前为止几乎所有内容,删除了所有并行性,并发队列到队列等。但问题仍然存在。

https://github.com/rmoergeli/ConsoleAppConcurrentMemoryProblem

VS和可执行文件,您必须"以管理员身份运行"。

看起来您正在尝试并行处理驱动器上的所有文件,并且可能最终缓存内存中的每个文件,因为工作线程方法最终会相互阻塞。在调试器下运行太慢,无法发生这种情况。

.NET 已经提供了一些机制,用于处理具有多个线程的步骤管道中的单个消息,限制一次内存中可以包含的项数。

很可能您只需要使用单个 ActionBlock 或 TransformBlock 类。这些类在其输入缓冲区中接受消息,并使用一个或多个工作器任务按顺序处理它们。它们还允许为其输入缓冲区设置边界,以避免在工作线程太慢时缓冲区溢出。

为了解决您的问题,也许您只需要一个带有DOP>1和有限输入缓冲区的ActionBlock,例如:

var options=new new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 5,
BoundedCapacity=10
});
var block=new ActionBlock<string>(filePath=>{
//Do something with that file
},options);
//Feed all files to the block
foreach(var file in Directory.EnumerateFiles("C:\","*",SearchOptions.AllDirectories))
{
await block.SendAsync(file);
}
block.Complete();
await block.Completion;

这会将所有文件馈送到块,如果输入缓冲区中等待的项目超过 10 个,则等待。每个项目都将在单独的任务中进行处理。最多同时处理 5 个项目。最后,它将等待块完成对所有缓冲消息的处理,然后再退出。

C:文件夹包含无法访问的文件夹,如果您尝试读取这些文件夹,则会引发异常。若要避免这种情况,可以使用枚举选项代替SearchOptions

var enumOptions=new EnumerationOptions { 
IgnoreInaccessible=true, 
RecurseSubdirectories=true
};
foreach(var file in Directory.EnumerateFiles("C:\","*",options))
{
await block.SendAsync(file);
}

当前代码的问题

代码过于复杂,并且以不适当甚至冲突的方式使用多个并发和并行构造。看起来它正在尝试处理本地磁盘中的所有文件,但最终将每个文件(或至少每个文件名或 FileInfo 对象(加载到内存中,等待一些被阻止的方法处理它们。 最有可能的是,在Visual Studio中运行它太慢而无法表现出这种行为。

  • 对于初学者来说,BackgroundWorker 已经过时,自 2012 年以来完全被 Task 和进度报告类取代。那是8年。没有正当理由再使用它了。

  • 任务
  • 不是线程,它只是一个作业(任务(,它将在某个时候在线程上运行。它并不意味着长期存活,绝对不是要充当带有循环的线程。

  • PLINQ 和Parallel类的方法适用于并行性 - 使用所有可用内核对大量内存中数据进行 CPU 密集型处理。它们不适用于并发,其中需要同时执行不同的操作,尤其是不需要 CPU 的 IO 方案。

  • 添加信号量只能通过让所有这些构造相互阻止来使事情正常工作。

相关内容

最新更新