2014-11-03 110 views
0

我遇到了C#中的任務/線程管理問題,我想知道是否有對我的問題的簡單解決方案。c#Task.WhenAll(任務)和SemaphoreSlim - 如何知道所有任務已完全完成

在我的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; 

我的進度/任務完成處理程序檢查的狀態,如果OK,我提出這將觸發呼叫到thread-事件這需要進行一些文件寫入安全的方法:

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

我遇到的困難是,主要的「任務設置」方法在所有上傳任務完成其線程安全方法之前返回並退出,即我們在下面的返回語句中執行,而幾個任務仍然還沒有在OnItemUploadOperationCompleted方法中輪到他們。

await Task.WhenAll(uploadTasks); 
// all uploads complete! 
return; 

我試圖找出是否有更好的方法來做到這一點。是否有辦法確定所有任務已經「完全」完成,他們還沒有掛起並等待排隊進入線程安全操作?基本上我需要知道所有處理何時完成,包括每個任務的所有線程安全後處理。

似乎「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(); 
      } 
     } 
} 

這似乎終於爲我工作。我想知道它是一個理想的解決方案嗎?

回答

1

Progress類用於使用當前操作進度更新UI,並且該操作不應該關心這些更新是什麼或者它們何時完成。

你在這裏是一個延續;一些任務完成後需要完成的工作才能根據前一個任務的結果進行額外的工作。你應該使用ContinueWith方法。 (或async方法,因爲這將transformated爲延續)

考慮到所有的你有什麼,這其實是很簡單的:然後

uploadTasks[i] = uploads[i].StartAsync() 
    .AsTask(ct, progressCallback) 
    .ContinueWith(task => ProcessResult(task.Result)); 

ProcessResult方法可以處理這些結果,當您有在您解僱Progress實例時一直在做。

+0

謝謝,我認爲這可能只是我尋找的解決方案! – user1857360 2014-11-03 17:20:50

+1

出於上下文和例外的原因,我建議使用'await'而不是'ContinueWith' +'Result'。最簡潔的解決方案可能是創建一個單獨的「異步」方法,以進行上傳以及後處理。 – 2014-11-03 21:05:08

+0

其實,這並沒有完全解決我的問題。 – user1857360 2014-11-04 11:34:36

相關問題