2017-09-24 104 views
2

非常大量的任务,我使用下面的模式来进行大量的操作(可能有数百万)处理内存

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集合。从一些诊断运行中,我似乎已经建立了大约1Gb的内存用于〜100k个对象。

对上述模式可以做出任何改变来淘汰完成的任务,但仍然保留整体模式的节流效果吗?

我能想到的唯一事情就是对整个数据集进行分区/分批处理,以便上面的代码只能运行,例如, 1000个元素。这是最合适的方法吗?

UPDATE

所以,根据你的咨询亨克,我已经实现了以下内容:

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

不是一个具体的答案,但一)Task.WhenAll()保留一切记忆和b)你做了Task.Run (异步无效...)做一些异步I/O。使用不必要的线程。 –

+0

[consensus](https://stackoverflow.com/a/11565531/60761)是使用TPL Dataflow。还请阅读下面的答案。 –

+1

由于Add()调用,代码在内存中保存了很多任务。尽管100K-8个任务已经完成,但并不是非常有用。无Clear()也可见。考虑用CountdownEvent类来计算任务,而不是WhenAll。 –

回答

-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