2016-04-28 97 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

类似的问题导致类似的答案。我经常看到人们为了异步等待而苦苦挣扎。显然旧的答案还不够清楚,或者找不到。虽然我不是第一个被告知的人,但它确实帮助我了解发生了什么事。顺便说一下:你见过这个例子有改进吗? –