在内存中处理非常多的任务

时间:2017-09-24 09:27:51

标签: c# semaphore

我使用以下模式执行大量操作(可能是数百万)

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();

这似乎工作正常,并且整个时间内的内存配置文件相对较低。这个实现对你来说看起来不错吗?

1 个答案:

答案 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

)来利用生产者/消费者模式