任务.什么时候不等

Gab*_*iel 7 c# task async-await

我正在学习如何在控制台应用程序中使用异步函数,但无法使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);

    ...
}
Run Code Online (Sandbox Code Playgroud)

更新 - 功能定义:

    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;

    }
Run Code Online (Sandbox Code Playgroud)

更新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]);
}
Run Code Online (Sandbox Code Playgroud)

Lua*_*aan 7

你正在调用一种async void方法,这本身就意味着你无法await获得结果.无论何时省略await,都会打破同步链.该操作真正异步发生,而不是通过"重新同步" await.控件返回给调用者,而(将来的某个时间)操作将异步恢复.

记住,await是一个return.只有一致的使用await才能让你同步.停止使用async void- 将其更改为async Task并确保await正确结果.您的MainAsync方法也是如此.Taskvoid异步方法.

只有一种情况你应该看到async void,这是在遗留框架的事件处理程序的同步上下文中(例如在Winforms中).如果一个async方法可以返回一个Task,那真的,真的应该.不要破坏链条.


Har*_*lse 5

错误是您的 main 函数不等待过程 IncluiValores 的完成。您的主程序在 IncluiValores 过程完成之前完成。

由于这个错误,我假设您仍然无法理解使用 async-await 时会发生什么。

StackOverflow 上的某人(唉,我再也找不到它了),用下面的比喻向我解释了这一点。

添加:我找到了隐喻,
它是在对 Eric Lippert 的采访中
在中间的某个位置搜索 async-await
End Adition

假设你需要做早餐。你想烤一些面包,煮一些鸡蛋,泡一些茶。

同步

  • 将面包放入烤面包机中,等待面包烤熟
  • 从烤面包机中取出面包。
  • 开始烧水,等到水沸腾
  • 将一些鸡蛋放入沸水中,等待 7 分钟,直到鸡蛋准备好
  • 将鸡蛋从水中取出
  • 开始煮茶水并等待水沸腾
  • 水沸腾后,将其放入茶壶中,加入一些茶叶,等待 4 分钟
  • 最后,您将所有东西放在一起并带到您的早餐桌上。

你会发现你等待了很多时间,这是浪费时间,更不用说茶喝完时你的面包可能已经冷了。

如果你不是一直等待,而是同时开始,效率会高得多

using async-await:使用一个线程异步

  • 与同步情况一样开始:将面包放入烤面包机中
  • 但现在你不用等到面包烤好。记住烤面包时你应该做什么(记住这是任务 A)
  • 开始烧水,但不要等到水烧开。记住当水沸腾时你应该做什么(记住这是任务 B)
  • 开始烧水泡茶,但不要等服务员烧开。记住当茶壶沸腾时你应该做什么(记住这是任务 C)

  • 等待任务 A / B / C 中的任何一个完成。继续你记得任务完成后应该做什么。如果这需要其他等待(鸡蛋或茶准备好的时间),请不要等待,而是将其记为任务 D 和 E,并开始等待所有未完成的任务。

请注意,在这种方法中仍然只有一个人做所有的事情。如果您以这种方式使用 async-await,则只涉及一个线程。该线程仅在确实无事可做时才会等待。这样做的优点是您不会遇到使用多个线程时通常遇到的问题。

使用多个线程异步

你可以雇佣几名厨师:一名负责烤面包,一名负责在泡茶时煮鸡蛋。这是一种昂贵的方法:启动多个线程,而线程大部分时间除了等待之外什么都不做。您还面临三个厨师必须同步的问题,以确保他们不会同时使用一火炉子。

Stephen Cleary写了一篇综合文章,描述了Async 和 Await中的这种异步等待行为(谢谢 Stephen!)

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();
}
Run Code Online (Sandbox Code Playgroud)

现在您可能已经知道如何泡茶了。