简体   繁体   English

这些C#异步方法为什么不通过Task.Delay()执行?

[英]Why don't these C# async methods execute past Task.Delay()?

I'm trying to understand C# async/await and observed this confusing behavior where async methods don't execute past Task.Delay calls. 我试图了解C#异步/等待,并观察到这种令人困惑的行为,其中异步方法不执行过去的Task.Delay调用。

Consider the below - 考虑以下-

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.MakeBreakfast();
    }

    public async Task MakeBreakfast()
    {
        await BoilWater();
        StartToaster();
        PutTeainWater();
        PutBreadinToaster();
        SpreadButter();
    }

    public async Task BoilWater() { Console.WriteLine("BoilWater start"); await Task.Delay(30); Console.WriteLine("BoilWater end"); }
    public async Task StartToaster() { Console.WriteLine("StartToaster start"); await Task.Delay(1); Console.WriteLine("StartToaster end"); }
    public async Task PutBreadinToaster() { Console.WriteLine("PutBreadinToaster start"); await Task.Delay(2000); Console.WriteLine("PutBreadinToaster end"); }
    public async Task PutTeainWater() { Console.WriteLine("PutTeainWater start"); await Task.Delay(30); Console.WriteLine("PutTeainWater end"); }
    public async Task SpreadButter() { Console.WriteLine("SpreadButter start"); await Task.Delay(10); Console.WriteLine("SpreadButter end"); }
}

Its output will be - 其输出将是-

 Boilwater Start Press any key to continue... 

What happened to "Boilwater end" statement and all other method calls? “ Boilwater end”语句和所有其他方法调用发生了什么? If I put async/await only in the BoilWater method, I get the same output. 如果仅将Async / await放在BoilWater方法中,则会得到相同的输出。

If I remove await from all method calls - 如果我从所有方法调用中删除了await-

    public async Task MakeBreakfast()
    {
         BoilWater();
         StartToaster();
         PutTeainWater();
         PutBreadinToaster();
         SpreadButter();
    }
 Now, the output is - BoilWater start StartToaster start PutTeainWater start PutBreadinToaster start SpreadButter start Press any key to continue . . . 

Now, what happened to "end" statements? 现在,“结束”语句发生了什么? What's going on with async await in these examples? 在这些示例中,异步等待发生了什么?

You program starts with the call to Main and it exits when it is done. 您的程序从对Main的调用开始,并在完成时退出。

Since Main just creates an instance of Program and then calls MakeBreakfast() , which returns a Task back to main as soon as it hits its first await . 由于Main只是创建的实例Program ,然后调用MakeBreakfast()它返回一个Task ,只要它击中了第一次回到主await Thus Main exists almost immediately. 因此Main几乎立即存在。

Let's change the code slightly to see if this is the case: 让我们稍微更改一下代码,看看是否是这种情况:

static void Main(string[] args)
{
    Program p = new Program();
    p.MakeBreakfast();
    Console.WriteLine("Done!");
    Console.ReadLine();
}

public async Task MakeBreakfast()
{
    Console.WriteLine("Starting MakeBreakfast");
    Thread.Sleep(1000);
    Console.WriteLine("Calling await BoilWater()");
    await BoilWater();
    Console.WriteLine("Done await BoilWater()");
    StartToaster();
    PutTeainWater();
    PutBreadinToaster();
    SpreadButter();
}

Now if I let this run to completion I see this output: 现在,如果我让它运行完成,我将看到以下输出:

Starting MakeBreakfast
Calling await BoilWater()
BoilWater start
Done!
BoilWater end
Done await BoilWater()
StartToaster start
PutTeainWater start
StartToaster end
PutBreadinToaster start
SpreadButter start
SpreadButter end
PutTeainWater end
PutBreadinToaster end

The code does indeed hit the await and then return to Main . 该代码确实确实处于await ,然后返回Main

To make the code complete correctly we need to await everything. 为了使代码正确完成,我们需要await一切。 You have two ways you can do this: 您可以通过两种方式执行此操作:

(1) (1)

static async Task Main(string[] args)
{
    Program p = new Program();
    await p.MakeBreakfast();
    Console.WriteLine("Done!");
    Console.ReadLine();
}

public async Task MakeBreakfast()
{
    await BoilWater();
    await StartToaster();
    await PutTeainWater();
    await PutBreadinToaster();
    await SpreadButter();
}

Now when this is run you get this output: 现在,当运行此命令时,您将获得以下输出:

BoilWater start
BoilWater end
StartToaster start
StartToaster end
PutTeainWater start
PutTeainWater end
PutBreadinToaster start
PutBreadinToaster end
SpreadButter start
SpreadButter end
Done!

(2) (2)

static async Task Main(string[] args)
{
    Program p = new Program();
    await p.MakeBreakfast();
    Console.WriteLine("Done!");
    Console.ReadLine();
}

public async Task MakeBreakfast()
{
    var tasks = new[]
    {
        BoilWater(),
        StartToaster(),
        PutTeainWater(),
        PutBreadinToaster(),
        SpreadButter(),
    };
    await Task.WhenAll(tasks);
}

Now this version starts all of the breakfast tasks at the same time but waits for them all to finish before returning. 现在,此版本同时启动所有早餐任务,但等待它们全部完成后再返回。

You get this output: 您得到以下输出:

BoilWater start
StartToaster start
PutTeainWater start
PutBreadinToaster start
SpreadButter start
StartToaster end
SpreadButter end
BoilWater end
PutTeainWater end
PutBreadinToaster end
Done!

An alternative which gives a more logical execution of the code - boil then water, then make the tea; 另一种方法可以使代码更符合逻辑地执行-先煮开水,然后煮茶; and start toaster, cook toast, spread the toast - might be like this: 然后开始烤面包机,煮吐司,传播吐司-可能是这样的:

public async Task MakeBreakfast()
{
    async Task MakeTea()
    {
        await BoilWater();
        await PutTeainWater();      
    }

    async Task MakeToast()
    {
        await StartToaster();
        await PutBreadinToaster();
        await SpreadButter();           
    }       
    await Task.WhenAll(MakeTea(), MakeToast());
}

That gives: 这给出了:

BoilWater start
StartToaster start
StartToaster end
PutBreadinToaster start
BoilWater end
PutTeainWater start
PutTeainWater end
PutBreadinToaster end
SpreadButter start
SpreadButter end
Done!

The general workflow of an async method is that the code until an await is run synchronously (ie as is), then a task object is returned with the task to be awaited and all the stuff after the await is put as a continuation of this task which is to be executed when the task completes. 异步方法的一般工作流程是直到同步运行等待之前的代码(即按原样),然后返回任务对象以及要等待的任务,并将等待后的所有内容作为该任务的延续当任务完成时执行。

Now, if only BoilWater is awaited the start message is executed synchronously and all the other calls are put as a continuation. 现在,如果仅等待BoilWater则开始消息将同步执行,而所有其他调用将作为继续。 As MakeBreakfast isn't awaited the program will execute before BoilWater can complete/wait their milliseconds, and thus the continuations (ie other tasks) are not executed. 由于MakeBreakfast不等待该程序将执行之前BoilWater可以完成/等待他们毫秒,从而延续(即其他任务)不会执行。

If BoilWater isn't awaited, the other MakeBreakfast tasks are not put as a continuation of the BoilWater task. 如果BoilWater不等待,其他MakeBreakfast任务都放不作为的延续BoilWater任务。 This means that BoilWater again runs until the Task.Delay and returns this as a task. 这意味着BoilWater再次运行,直到Task.Delay并将其作为任务返回。 However, as this task isn't awaited, the next MakeBreakfast is started in the same manner. 但是,由于不等待此任务,因此以相同的方式启动下一个MakeBreakfast So essentially, all MakeBreakfast tasks are started sequentially and MakeBreakfast can only return when SpreadWater is started and returns it's task. 因此,从本质MakeBreakfast ,所有MakeBreakfast任务均按顺序启动,并且MakeBreakfast仅在SpreadWater启动并返回其任务时SpreadWater返回。 Again, the tasks still run in the background waiting their milliseconds but the program exits before this timeframe and thus the closing message continuations have no chance of being run. 同样,任务仍在后台运行,等待其毫秒数,但是程序在此时间段之前退出,因此关闭消息继续运行的机会不大。

What happened to "Boilwater end" statement and all other method calls? “ Boilwater end”语句和所有其他方法调用发生了什么? If I put async/await only in the BoilWater method, I get the same output. 如果仅将Async / await放在BoilWater方法中,则会得到相同的输出。

As was zerkms said, your program exits before your task is complete. 正如zerkms所说的那样,您的程序在任务完成之前就退出了。

If I remove await from all method calls - 如果我从所有方法调用中删除了await-

I believe if await is not called anywhere within an async Task, then it just processes it synchronously; 我相信,如果异步任务中的任何地方都没有调用await ,那么它只会同步处理它; this would explain why the output shows all the "start" messages. 这可以解释为什么输出显示所有“开始”消息。

As for what happened to the "end" statements, I believe that if you do not await Task.Delay , then it wont actually wait (delay) and your code will continue. 至于“ end”语句发生了什么,我相信如果您不等待Task.Delay ,那么它实际上不会等待(延迟),您的代码将继续。 Actually I just found this which explains it better. 实际上,我只是发现了这一点 ,这可以更好地解释它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM