Visual studio C#.NET核心3.1代码在Visual Studio中运行时间长,但发布的单个文件可执行文件消耗了大量内存
我想发布我的应用程序,因为它运行良好。但当我把它作为一个打包的应用程序运行时,它使用了高达20GB甚至更多的巨大内存。在VisualStudio2019中运行它,我无法重现该问题。它运行的时间也很长,在VS中,即使有很多文件,也只需几秒钟 到目前为止,我尝试了我认为几乎所有的方法,删除了所有的并行性,ConcurrentQueue-to-Queue等,但问题仍然存在Visual studio C#.NET核心3.1代码在Visual Studio中运行时间长,但发布的单个文件可执行文件消耗了大量内存,visual-studio,.net-core,Visual Studio,.net Core,我想发布我的应用程序,因为它运行良好。但当我把它作为一个打包的应用程序运行时,它使用了高达20GB甚至更多的巨大内存。在VisualStudio2019中运行它,我无法重现该问题。它运行的时间也很长,在VS中,即使有很多文件,也只需几秒钟 到目前为止,我尝试了我认为几乎所有的方法,删除了所有的并行性,ConcurrentQueue-to-Queue等,但问题仍然存在 对于VS和可执行文件,您必须“以管理员身份运行”。看起来您试图并行处理驱动器上的所有文件,并且可能最终缓存内存中的每个文件,因为
对于VS和可执行文件,您必须“以管理员身份运行”。看起来您试图并行处理驱动器上的所有文件,并且可能最终缓存内存中的每个文件,因为辅助方法最终会相互阻塞。在调试器下运行太慢,无法发生这种情况 NET已经提供了一些机制,用于在具有多个线程的步骤管道中处理单个消息,并限制一次可以在内存中存储多少项 很可能您只需要使用一个或多个类。这些类在其输入缓冲区中接受消息,并使用一个或多个辅助任务按顺序处理它们。它们还允许为输入缓冲区设置边界,以避免缓冲区溢出(如果工作进程太慢) 为了解决您的问题,您可能只需要一个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;
当前代码存在问题
代码过于复杂,以不适当甚至冲突的方式使用多个并发和并行结构。看起来它试图处理本地磁盘中的所有文件,但最终加载内存中的每个文件(或至少每个文件名或FileInfo对象),等待一些被阻止的方法来处理它们。最有可能的是,在VisualStudio中运行此操作太慢,无法显示此行为
- 首先,BackgroundWorker已经过时,自2012年以来完全被用于进度报告的Task和Progress类所取代。那是8年。没有充分的理由再使用它了
- 一个任务不是一个线程,它只是一个将在某个线程上运行的作业(任务)。它并不意味着要活很长时间,也绝对不意味着要充当一个带有循环的线程
- PLINQ和
类的方法旨在实现并行性,即使用所有可用内核对大量内存数据进行CPU密集型处理。它们不适用于并发性,即需要同时执行不同的操作,特别是不需要CPU的IO场景Parallel
- 添加信号量只能通过让所有这些构造相互阻塞来实现
Task.Run
取代。如果要报告进度,请使用进度
类。任务也不是线程,它们表示将在线程上运行的作业(任务),因此不应该永远运行。最后,PLINQ
和Parallel
用于并行处理大量数据,即使用所有可用的内核。它们并不意味着要同时处理内容如果你想处理大量文件,也许你所需要的只是使用一个并行度设置为>1的ActionBlock
,并将文件以循环方式发送给它。ActionBlock已经有了一个输入队列,所以您不需要ConcurrentQueue或semaphoresEven,它们可以用缓存对象快速填充内存。也许这就是这里发生的情况-VS运行得太慢,您无法注意到,但在发布版本中,所有本地文件最终都会缓存在内存中,因为由于信号量、任务和PLINQ相互阻塞,工作方法处理它们的速度太慢。要解决此问题,请将块的BoundedCapacity
设置为固定数字,而不是无限
var enumOptions=new EnumerationOptions {
IgnoreInaccessible=true,
RecurseSubdirectories=true
};
foreach(var file in Directory.EnumerateFiles("C:\\","*",options))
{
await block.SendAsync(file);
}