[英]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.