我使用以下模式执行大量操作(可能是数百万)
var allTasks = new List<Task>();
var throttler = new SemaphoreSlim(initialCount: 8);
foreach (var file in filesToUpload)
{
await throttler.WaitAsync();
allTasks.Add(
Task.Run(async () =>
{
try
{
await UploadFileAsync(file)
}
finally
{
throttler.Release();
}
}));
}
await Task.WhenAll(allTasks);
但是,我担心在Task
集合中积累大量allTasks
个对象。从一些诊断运行开始,我似乎已经为~100k Task
个对象构建了大约1Gb的内存。
是否可以对上述模式进行任何更改以逐步淘汰已完成的任务,但仍保留整体模式的限制效果?
我唯一可以想到的就是对整个数据集进行分区/批处理,以便上述代码只能运行,例如, 1000个元素。这是最合适的方法吗?
更新
所以,根据你的建议Henk,我已经实现了以下内容;
var uploadFileBlock = new ActionBlock<string>(async file =>
{
await UploadFileAsync(file)
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 8 });
foreach (var file in filePaths)
{
await uploadFileBlock.SendAsync(file);
}
uploadFileBlock.Completion.Wait();
这似乎工作正常,并且整个时间内的内存配置文件相对较低。这个实现对你来说看起来不错吗?
答案 0 :(得分:-2)
这与another recent SO questions非常相似。与那个问题一样,可行的方法(虽然我自己没有测试过)将是:
private async Task Test()
{
var allTasks = new List<Task>();
foreach (var file in filesToUpload)
{
await WaitList(allTasks, 1000);
allTasks.Add(UploadFileAsync(file));
}
await Task.WhenAll(allTasks);
}
private async Task WaitList(IList<Task> tasks, int maxSize)
{
while (tasks.Count > maxSize)
{
var completed = await Task.WhenAny(tasks).ConfigureAwait(false);
tasks.Remove(completed);
}
}
这样的批处理不仅有助于内存,还可以帮助您避免无意中拒绝服务附加。
其他方法可能会使用.Net类(如BlockingCollection