[英]c# Task.WhenAll(tasks) and SemaphoreSlim - how to know when all tasks have fully completed
我在C#中的任務/線程管理上遇到問題,我想知道是否有針對我的問題的簡單解決方案。
在Windows Phone應用程序中,我創建了一堆“上傳”任務。 每個任務都有一個附加的進度/完成處理程序,在其中檢查任務的狀態,如果成功完成,則需要執行許多線程安全的文件寫入操作。
偽代碼:
此代碼段來自設置我的任務並使其開始運行的方法。 我希望只有在所有任務都完全完成后,此方法才返回給調用方:
var progressCallback = new Progress<UploadOperation>(UploadProgress);
for (var i = 0; i < uploads.Count; i++)
{
uploadTasks[i] = uploads[i].StartAsync().AsTask(ct, progressCallback);
}
await Task.WhenAll(uploadTasks);
// all uploads complete!
return;
我的進度/任務完成處理程序檢查狀態,如果確定,我引發一個事件,該事件觸發對線程安全方法的調用,該方法需要執行一些文件寫入:
private void UploadProgress(UploadOperation upload){
....
if(upload == successful)
{
//raise an event which results in a call to a thread-safe method
//to perform post-upload tasks
}
....
}
這是我的線程安全方法(由上述事件觸發),在該方法中,我使用SemaphoreSlim對象來確保一次僅一個線程可以訪問它:
private static readonly SemaphoreSlim _SemaphoreSlim = new SemaphoreSlim(1);
private async void OnItemUploadOperationCompleted(...)
{
await _SemaphoreSlim.WaitAsync();
try
{
//perform some await-able file updates / writes
}
catch(..){}
finally
{
//release lock
_SemaphoreSlim.Release();
}
我遇到的困難是,主要的“任務設置”方法在所有上載任務都完成了線程安全方法之前就返回並退出,即我們在下面的return語句中找到了一些任務輪流使用OnItemUploadOperationCompleted方法。
await Task.WhenAll(uploadTasks);
// all uploads complete!
return;
我正在嘗試找出是否有更好的方法可以做到這一點。 有沒有一種方法可以確定所有任務是否“完全”完成,還沒有掛起並在隊列中等待進入線程安全操作? 基本上,我需要知道所有處理何時完成,包括每個Task的所有線程安全后處理。
似乎“ Task.WhenAll”返回的時間過早,也許是在初始Task本身完成時返回的,而不是在子任務/衍生任務完成時返回的?
編輯:
我遵循了Servy的建議(下面的第一個答案),如下所示:
foreach (var upload in uploads)
{
uploadTasks.Add(upload.StartAsync().AsTask(ct, progressCallback).ContinueWith(task => ProcessUploadResult(task.Result), ct));
}
await Task.WhenAll(uploadTasks);
// all uploads complete?
return;
我的ProcessUploadResult方法如下所示:
private void ProcessUploadResult(UploadOperation uploadResult){
....
//pseudo code
if(uploadResult == success){
//RAISE an Event!
//The listener of this event processes the result -
// - performs some file writes / updates
// - therefore the handler for this eventy MUST be thread safe.
OnItemUploadOperationCompleted(this, ...});
}
}
因此,即使使用這種方法,我的困難還是在於,事件處理程序到“ Task.WhenAll(...)”返回時仍未完成對所有上載的處理。 仍然有線程在等待訪問該事件處理程序。
所以,我想我已經找到了解決方案,並且想知道使用ManualResetEvent是否是一個好的解決方案:
....
//pseudo code
if(uploadResult == success){
//RAISE an Event!
//The listener of this event processes the result -
// - performs some file writes / updates
// - therefore the handler for this eventy MUST be thread safe.
var wait = new ManualResetEvent(false);
// pass the reference to ManualResetEvent in the event Args
OnItemUploadOperationCompleted(this, new MyEventArgs {waiter = wait});
wait.WaitOne();
}
}
現在,在我的處理程序中,我使用那個ManualResetEvent對象向等待的線程發送信號,表明我們已經處理完上傳響應:
private async void OnItemUploadOperationCompleted(object sender, UploadResultEventArgs e)
{
await _SemaphoreSlim.WaitAsync();
try
{
//perform async file writes
}
catch{
....
}
finally
{
//release lock
_SemaphoreSlim.Release();
//signal we are done here
var waiter = e.Waiter as ManualResetEvent;
if (waiter != null)
{
waiter.Set();
}
}
}
這似乎終於對我有用。 我想知道這是否是理想的解決方案?
那里有Progress
類,用於使用操作的當前進度來更新UI,並且該操作不必關心這些更新是什么或何時完成。
您在這里擁有的是延續。 任務完成后需要完成的一些工作才能根據上一個任務的結果執行其他工作。 您應該為此使用ContinueWith
方法。 (或async
方法,因為它將轉換為延續。)
考慮到您擁有的所有內容,這實際上非常簡單:
uploadTasks[i] = uploads[i].StartAsync()
.AsTask(ct, progressCallback)
.ContinueWith(task => ProcessResult(task.Result));
然后,您的ProcessResult
方法可以像觸發Progress
實例時一樣處理這些結果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.