2013-10-02 28 views
1

每次運行以下麥克異常被捕獲。是當所有順序或併發?

是WhenAll順序涉及各個任務之間的連續上下文或做得到同時運行所有任務?如果這是同時發生的,爲什麼麥克的例外總是被抓住,而不是米奇的。我推遲了邁克,以便給米奇一個機會。如果它是連續的,那麼它是如何並行的?在進行Web請求/進行文件處理時是否應用併發執行?

假設這個代碼是更嚴重的會變成這樣一個明智的做法,以異步?這種情況會有幾種方法--Jason,Mitch和Mike--在沒有阻塞的情況下同時運行,並在全部完成時繼續執行事件處理程序?我應該注意哪些有關我的天真執行異常處理的問題?有任何問題或潛在問題需要注意?

private async void button1_Click(object sender,EventArgs e) 
{ 
    try 
    { 
     AsyncJason c1 = new AsyncJason(); 
     await c1.Hello(); 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
    } 
} 

public class AsyncJason 
{ 
    public AsyncJason() 
    { 
    } 

    public async Task Hello() 
    { 
     var j = await GetJasonAsync(); 
     string[] dankeSchon = await Task.WhenAll(new Task<string>[] {GetJasonAsync(), GetMikeAsync(), GetMitchAsync()}); 
    } 

    private async Task<string> GetJasonAsync() 
    { 
     var result = await Task.Run<string>(() => GetJason()); 
     return result; 
    } 

    private string GetJason() 
    { 
     return "Jason"; 
    } 

    private async Task<string> GetMitchAsync() 
    { 
     var result = await Task.Run<string>(() => GetMitch()); 
     return result; 
    } 

    private string GetMitch() 
    { 
     throw new ArgumentException("Mitch is an idiot", "none"); 
    } 

    private async Task<string> GetMikeAsync() 
    { 
     await Task.Delay(3000); 
     var result = await Task.Run<string>(() => GetMike()); 
     return result; 
    } 

    private string GetMike() 
    { 
     throw new ArgumentException("Mike is an idiot", "none"); 
    } 
} 
+2

當沒有辦法控制任務如何執行時(即所有任務都可以在執行完時執行)...... –

回答

6

是WhenAll順序或併發?

的問題並沒有真正適用。當所有基礎任務完成時,WhenAll的任務已完成。它如何去做是它的事。

當涉及到的異常,TaskException屬性包含AggregateException具有由所有的基本任務,拋出的異常所有

當你await有代表多個異常就會展開,從而再次引發第一例外在該列表中,而不是AggregateException與所有在它的例外的集合異常的任務。

在創建AggregateException它(顯然,我不知道這是以往任何地方投放承諾)列出了基於傳遞給WhenAll,而不是基於爲了這些任務完成的任務的順序上的異常。

如果你擔心失去的例外,那麼你應該保存它返回,這樣就可以檢查所有的異常,或者只是重新拋出包裹AggregateException,即任務:

public async Task Hello() 
{ 
    var j = await GetJasonAsync(); 
    var task = Task.WhenAll(new Task<string>[] { GetJasonAsync(), GetMikeAsync(), GetMitchAsync() }); 
    try 
    { 
     string[] dankeSchon = await task; 
    } 
    catch (Exception) 
    { 
     throw task.Exception; 
    } 
} 

如果你真的想要首先被擊中的異常是可以重新拋出的異常。一種選擇是基本上重寫WhenAll是我們自己的版本,只是略微不同地處理異常。另一種選擇是根據它們將完成的順序排列任務,有趣的是,我們可以在保持異步並且對任務一無所知的情況下完成任務。下面是一個Order方法基於完成時間是需要的任務序列,並返回代表相同的操作任務的順序,但排序(升序)。

public static IEnumerable<Task<T>> Order<T>(this IEnumerable<Task<T>> tasks) 
{ 
    var taskList = tasks.ToList(); 

    var taskSources = new BlockingCollection<TaskCompletionSource<T>>(); 

    var taskSourceList = new List<TaskCompletionSource<T>>(taskList.Count); 
    foreach (var task in taskList) 
    { 
     var newSource = new TaskCompletionSource<T>(); 
     taskSources.Add(newSource); 
     taskSourceList.Add(newSource); 

     task.ContinueWith(t => 
     { 
      var source = taskSources.Take(); 

      if (t.IsCanceled) 
       source.TrySetCanceled(); 
      else if (t.IsFaulted) 
       source.TrySetException(t.Exception.InnerExceptions); 
      else if (t.IsCompleted) 
       source.TrySetResult(t.Result); 
     }, CancellationToken.None, TaskContinuationOptions.PreferFairness, TaskScheduler.Default); 
    } 

    return taskSourceList.Select(tcs => tcs.Task); 
} 

基本上這裏的想法是創建每個任務TaskCompletionSource,延續添加到每個提供給我們的任務,然後在任何任務完成後,我們紀念這不是尚未完成的TaskCompletionSource什麼剛剛完成的任務的結果是。

使用這個,我們現在可以這樣寫:

public async Task Hello() 
{ 
    var j = await GetJasonAsync(); 
    var tasks = new[] { GetJasonAsync(), GetMikeAsync(), GetMitchAsync() }; 
    string[] dankeSchon = await Task.WhenAll(tasks.Order()); 
} 

和異常將是首先被拋出的異常的。

+0

不會拋出task.Exception異常的堆棧跟蹤? –

+0

很好的解釋謝謝! –

+0

@ScottChamberlain聚合異常,是的。在內部的例外中,我不會這麼期待。我記得看到一個C#5.0工具的例子,可以用來在不清除堆棧的情況下重新拋出一個異常,但我沒有花時間去查找它;理論上可以在這裏應用。 – Servy

相關問題