我试图理解 C# async/await 并观察到这种令人困惑的行为,其中异步方法不执行过去的Task.Delay调用。
考虑以下 -
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"); }
}
Run Code Online (Sandbox Code Playgroud)
它的输出将是 -
Run Code Online (Sandbox Code Playgroud)Boilwater Start Press any key to continue...“Boilwater end”语句和所有其他方法调用发生了什么变化?如果我只将 async/await 放在 BoilWater 方法中,我会得到相同的输出。
如果我从所有方法调用中删除 await -
public async Task MakeBreakfast()
{
BoilWater();
StartToaster();
PutTeainWater();
PutBreadinToaster();
SpreadButter();
}
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)Now, the output is - BoilWater start StartToaster start PutTeainWater start PutBreadinToaster start SpreadButter start Press any key to continue . . .现在,“结束”语句发生了什么?在这些示例中,异步等待发生了什么?
您的程序从调用开始,Main并在完成后退出。
由于Main只是创建了一个 的实例,Program然后调用MakeBreakfast(),Task它会在遇到第一个 时立即返回到 main await。因此Main几乎立即存在。
让我们稍微更改一下代码,看看是否是这种情况:
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();
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我让它运行完成,我会看到以下输出:
开始制作早餐 调用 await BoilWater() 开水开始 完毕! 沸水端 完成等待 BoilWater() 开始烤面包机开始 PutTeainWater 开始 开始烤面包机结束 PutBreadinToaster 启动 涂抹黄油开始 涂抹黄油结束 放茶水端 PutBreadinToaster 结束
代码确实命中了await然后返回到Main.
为了使代码正确完成,我们需要await一切。您有两种方法可以做到这一点:
(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();
}
Run Code Online (Sandbox Code Playgroud)
现在当它运行时,你会得到这个输出:
开水开始 沸水端 开始烤面包机开始 开始烤面包机结束 PutTeainWater 开始 放茶水端 PutBreadinToaster 启动 PutBreadinToaster 结束 涂抹黄油开始 涂抹黄油结束 完毕!
(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);
}
Run Code Online (Sandbox Code Playgroud)
现在这个版本同时开始所有的早餐任务,但在返回之前等待它们全部完成。
你得到这个输出:
开水开始 开始烤面包机开始 PutTeainWater 开始 PutBreadinToaster 启动 涂抹黄油开始 开始烤面包机结束 涂抹黄油结束 沸水端 放茶水端 PutBreadinToaster 结束 完毕!
一种更合乎逻辑地执行代码的替代方案 - 先煮沸水,然后泡茶;然后启动烤面包机,烤面包,铺烤面包 - 可能是这样的:
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());
}
Run Code Online (Sandbox Code Playgroud)
这给出了:
开水开始 开始烤面包机开始 开始烤面包机结束 PutBreadinToaster 启动 沸水端 PutTeainWater 开始 放茶水端 PutBreadinToaster 结束 涂抹黄油开始 涂抹黄油结束 完毕!