2014-11-03 68 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

相关问题