[英]C# Parallel.ForEach() memory usage keeps growing
public string SavePath { get; set; } = @"I:\files\";
public void DownloadList(List<string> list)
{
var rest = ExcludeDownloaded(list);
var result = Parallel.ForEach(rest, link=>
{
Download(link);
});
}
private void Download(string link)
{
using(var net = new System.Net.WebClient())
{
var data = net.DownloadData(link);
var fileName = code to generate unique fileName;
if (File.Exists(fileName))
return;
File.WriteAllBytes(fileName, data);
}
}
var downloader = new DownloaderService();
var links = downloader.GetLinks();
downloader.DownloadList(links);
我猜 Parallel.ForEach() 有问题,但我想不通。
是否存在内存泄漏,或者发生了什么?
更新 1
改成新代码后
private void Download(string link)
{
using(var net = new System.Net.WebClient())
{
var fileName = code to generate unique fileName;
if (File.Exists(fileName))
return;
var data = net.DownloadFile(link, fileName);
Track theTrack = new Track(fileName);
theTrack.Title = GetCDName();
theTrack.Save();
}
}
在保持运行 9 小时后,我仍然观察到内存使用量增加,但使用量增长缓慢。
只是想知道,是不是因为我没有释放 theTrack 文件的内存使用?
顺便说一句,我使用ALT 包来更新文件元数据,不幸的是,它没有实现 IDisposable 接口。
使用WebClient.DownloadFile()
直接下载到文件,这样您就不会在内存中保存整个文件。
Parallel.ForEach
方法旨在并行化受 CPU 限制的工作负载。 下载文件是 I/O 绑定的工作负载,因此Parallel.ForEach
不适合这种情况,因为它不必要地阻塞了ThreadPool
线程。 正确的做法是异步的,使用 async/await。 推荐的异步 Web 请求类是HttpClient
,控制并发级别的一个很好的选择是TPL 数据流库。 对于这种情况,使用这个库中最简单的组件ActionBlock
类就足够了:
async Task DownloadListAsync(List<string> list)
{
using (var httpClient = new HttpClient())
{
var rest = ExcludeDownloaded(list);
var block = new ActionBlock<string>(async link =>
{
await DownloadFileAsync(httpClient, link);
}, new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 10
});
foreach (var link in rest)
{
await block.SendAsync(link);
}
block.Complete();
await block.Completion;
}
}
async Task DownloadFileAsync(HttpClient httpClient, string link)
{
var fileName = Guid.NewGuid().ToString(); // code to generate unique fileName;
var filePath = Path.Combine(SavePath, fileName);
if (File.Exists(filePath)) return;
var response = await httpClient.GetAsync(link);
response.EnsureSuccessStatusCode();
using (var contentStream = await response.Content.ReadAsStreamAsync())
using (var fileStream = new FileStream(filePath, FileMode.Create,
FileAccess.Write, FileShare.None, 32768, FileOptions.Asynchronous))
{
await contentStream.CopyToAsync(fileStream);
}
}
使用HttpClient
下载文件的代码不像WebClient.DownloadFile()
那样简单,但这是您必须执行的操作,以保持整个过程异步(从 Web 读取和写入磁盘)。
警告:异步文件系统操作目前在 .NET 中没有有效实现。 为了获得最大效率,最好避免在FileStream
构造函数中使用FileOptions.Asynchronous
选项。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.