2016-04-28 157 views
7

我正在學習如何在控制檯應用程序中使用異步函數,但無法使Task.WhenAll等待所有任務完成。下面的代碼有什麼問題?它同步工作。先謝謝你。Task.WhenAll不等待

static void Main(string[] args) 
{ 
    ... 
    IncluiValores(...); 
    ... 
} 

static async void IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    List<int>[] res = await Task.WhenAll(res1, res2); 

    ... 
} 

更新 - 功能定義:

public async Task<List<int>> GetAIDBAPI(Attributes attributes) 
    { 

     List<int> results = null; 

     Connections client0 = new Connections(); 
     HttpClient client = client0.OpenAPIConnection(attributes.User[0], attributes.Pwd, attributes.Server, attributes.Chave, attributes.Server2); 
     HttpResponseMessage response = await client.PostAsJsonAsync("api/Attributes/ID/Bulk", attributes); 

     if (response.IsSuccessStatusCode) 
     { 
      var content = await response.Content.ReadAsStringAsync(); 
      results = JsonConvert.DeserializeObject<dynamic>(content).ToObject<List<int>>(); 
     } 
     else 
     { 
      var content = "[{-1}]"; 
      var result = JsonConvert.DeserializeObject<dynamic>(content); 
      results = result.ToObject<List<int>>(); 
     } 

     return results; 

    } 

更新2 - 單獨的上下文

static void Main(string[] args) 
{ 
    AsyncContext.Run(() => MainAsync(args)); 
} 

static async void MainAsync(string[] args) 
{ 
    await IncluiValores(...); 
} 

static async Task IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    List<int>[] res = await Task.WhenAll(res1, res2); // <- Error here 
    //Collection was modified; enumeration operation may not execute 
    ... 
} 
//Tried to change to code below but it does not wait. 
static async Task IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    await Task.WhenAll(res1, res2); // <- No error, just doesn't wait. 
    list.Add(res1.Result[0]); 
} 
+0

GetAIDBAPI的定義是什麼樣的? – BoltClock

+0

包含定義。 – Gabriel

+0

您的呼叫代碼位於何處?如果它在Main中,它將無法正常工作。如果它使用自己的異步方法,那麼該方法的任務是如何開始的? – BoltClock

回答

4

你調用一個async void方法,它本質上意味着你沒有的await方式的荷蘭國際集團結果。無論何時你忽略await,你都打破了同步鏈。該操作是真正異步發生的,而不是通過await「重新同步」。該控件返回給調用者,而(在將來某個時候)該操作異步恢復。

請記住,awaitreturn。這只是await的一致使用,它可以爲您提供同步。停止使用async void - 將其更改爲async Task,並確保您的await結果正確。這同樣適用於您的MainAsync方法。 Task是異步方法的void

只有一種情況你應該看到async void,這是在遺留框架的事件處理程序(例如Winforms)的同步上下文中。如果async方法有可能返回Task,那真的應該。不要打破這個鏈條。

2

錯誤是您的主要功能不等待完成程序inclusiValores。您的主程序在程序包含完成之前完成。

由於這個錯誤,我假設您在使用異步等待時會發生什麼情況仍然有些麻煩。

有人在StackOverflow(唉,我找不到它了),用下面的比喻向我解釋。

另:我發現metaphore
It is in this interview with Eric Lippert
搜索某個地方,中間爲異步等待
末ADITION

假設你需要做早餐。你想烤一些麪包,煮一些雞蛋,喝點茶。在烤麪包機

同步

  • 把麪包,等到麪包烤
  • 從烤箱中取出麪包。
  • 開始燒開水,等到水沸騰
  • 放一些雞蛋在沸水中並等待7分鐘,直到你的雞蛋準備
  • 從水中取出雞蛋
  • 開始沸水的茶和等到水沸騰
  • 當水沸騰時,你把它放在茶壺裏,加入一些茶葉,等待4分鐘
  • 最後,你把所有東西放在一起,把它帶到你的早餐桌上。

你看到你做了很多等待,這是浪費時間,更不用說你的麪包在茶完成時可能很冷。

這將是更有效的,如果你沒有等到所有的時間,但會採用異步等待同時

開始做事:異步使用一個線程

  • 開始作爲同步案例:將麪包放入烤麪包機
  • 但現在您不要等到麪包烤完。記住麪包烘烤時應該做的事(記住這是任務A)
  • 開始煮沸水,但不要等待水沸騰。記住當水沸騰時你應該做什麼(記住這是任務B)
  • 開始爲你的茶開水,但不要等待服務員煮沸。記住茶壺煮沸時應該做什麼(記住這是任務C)

  • 等到任何A/B/C任務完成。繼續你記得當任務完成時你應該做什麼。如果需要其他的等待時間(雞蛋或茶準備好的時間),不要等待它,但記住它作爲任務D和E,並開始等待所有未完成的任務。

請注意,在這種方法中,仍然只有一個人在做所有的東西。如果以這種方式使用異步等待,則只涉及一個線程。這個線程只在等待,如果它真的沒有任何關係。這樣做的好處是,您不會遇到使用多個線程時通常遇到的問題。

異步使用多個線程

你可以僱幾個廚師:一個是烤麪包和一個當你做出TEAD對熟蛋。這是一個昂貴的方法:啓動幾個線程,而線程什麼也不做,只能等待大部分時間。您還有三位廚師必須同步的問題,以確保他們不會同時使用單火爐。

斯蒂芬·克利裏寫道描述Async and Await這個異步等待狀態的全面文章(謝謝斯蒂芬!)

static void Main(string[] args) 
{ 
    var breakFast = await Task.Run(() => MakeBreakFast()); 
    // once here I know breakfast is ready 
    Eat(breakFast); 
} 
private static async Task<BreakFast> MakeBreakFast() 
{ 
    var taskToastBread = ToastBreadAsync(); 
    // do not await. As soon as the procedure awaits come back to do the next statement: 
    var taskBoilEggs = BoilEggsAsync(); 
    // again do not await. Come back as the procedure awaits 
    var taskMakeTea = MakeTeaAsync(); 
    // do not wait, but come bask as soon as the procedure await 

    // now wait until all three tasks are finished: 
    await Task.WhenAll (new Task[] {taskToasBread, taskBoilEggs, taskMakeTea}); 
    // if here: all tasks are finished. Property Result contains the return value of the Task: 
    return new BreakFast() 
    { 
     Toast = taskToastBread.Result, 
     Eggs = taskBoilEggs.Result, 
     Tea = taksMakeTea.Result, 
    } 
} 

private static Task<Toast> ToastBreadAsync() 
{ 
    var sliceOfBread = Loaf.CutSliceOfBread(); 
    Toaster.Insert(sliceOfBread); 
    await Toaster.Toast(); 
    // the function does not wait but return to the caller. 
    // the next is done when the caller await and the toaster is ready toasting 
    var toast = Toaster.Remove(); 
    return Toast(); 
} 

private static Task<Eggs> BoilEggsAsync() 
{ 
    var eggPan = ... 
    await eggPan.BoilWater(); 
    var eggs = Fridge.ExtreactEggs(); 
    EggPan.Insert(eggs); 
    await Task.Delay(TimeSpan.FromMinutes(7)); 
    return EggPan.Remove(); 
} 

你可能會知道,現在如何使茶。

+0

我已經讀過這個複製粘貼的「製作早餐」的答案,現在我已經知道絕對*一切*要知道關於烘烤麪包和煮雞蛋(當然並行)。 –

+0

類似的問題導致類似的答案。我經常看到人們爲了異步等待而苦苦掙扎。顯然舊的答案還不夠清楚,或者找不到。雖然我不是第一個被告知的人,但它確實幫助我瞭解發生了什麼事。順便說一下:你見過這個例子有改進嗎? –