2017-09-24 129 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