【问题标题】:async programming trying to understand how await works异步编程试图了解 await 是如何工作的
【发布时间】:2018-09-15 10:51:39
【问题描述】:

所以我正在学习一些基本的异步编程并遵循本教程: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/ 但是我得到的输出与我的预期不同。

这是我的代码:

private async void btn1_Click(object sender, EventArgs e)
{
   await TestAsync();
   Console.WriteLine("terminate");
}

private async Task TestAsync()
{
   string str = await Todo();
   await Task.Delay(500);  //added await operator as per FCin advice
   Console.WriteLine(str);
}

private async Task<string> Todo()
{
   await Task.Delay(3000); //added await operator as per FCin advice
   return "return from async task";
}

从单击 btn1 开始,将触发 btn1_Click 方法。 首先它将调用TestAsync()。 由于TestAsync方法的第一行是等待一个async方法,所以我的理解是此时await操作符应该暂停TestAsync并将控制权返回给TestAsync的调用者,即btn1_Click。 这应该打印“终止”,然后程序应该耐心等待 TestAsync 完成并最终打印“从异步任务返回”。 但是我得到的输出是相反的顺序,我试图理解为什么。

所以我对其进行了修改,以便现在在 btn1_Click 方法中等待 TestAsync。我也将线程睡眠更改为任务延迟,但我仍然得到相同的输出......

Edit2:我使用的代码作为示例

// 1. Three things to note in the signature:  
//  - The method has an async modifier.   
//  - The return type is Task or Task<T>. (See "Return Types" section.)  
//    Here, it is Task<int> because the return statement returns an integer.  
//  - The method name ends in "Async."  
async Task<int> AccessTheWebAsync()  
{   
// 2. You need to add a reference to System.Net.Http to declare client.  
HttpClient client = new HttpClient();  

// 3. GetStringAsync returns a Task<string>. That means that when you await 
// the task you'll get a string (urlContents).  
Task<string> getStringTask = 
client.GetStringAsync("http://msdn.microsoft.com");  

// 4 .You can do work here that doesn't rely on the string from 
//GetStringAsync.  
DoIndependentWork();  

// 5. The await operator suspends AccessTheWebAsync.  
//  - AccessTheWebAsync can't continue until getStringTask is complete.  
//  - Meanwhile, control returns to the caller of AccessTheWebAsync.  
//  - Control resumes here when getStringTask is complete.   
//  - The await operator then retrieves the string result from getStringTask.  
string urlContents = await getStringTask;  

// 6. The return statement specifies an integer result.  
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.  
return urlContents.Length;  
} 

【问题讨论】:

  • 您永远不会在btn1_Click 中等待对TestAsync 的调用,因此您基本上会触发/忘记返回的任务。
  • 也不要使用Thread.Sleep,而是使用await Task.Delay
  • 调试器是理解这一点的好工具。逐行浏览代码,它会显示发生了什么。如前所述,Thread.Sleep 只会阻止执行,不会让任何事情继续进行。
  • 只有当它实际上必须等待任务完成时才会返回控制,在你的 Todo 方法中没有等待异步操作,所以它像没有异步/等待一样运行
  • 不断更改问题中的代码使其非常难以理解。例如。就目前而言,它使 FCins 的答案难以理解,因为现在您的代码 看起来与他们答案中的代码一模一样。而且您的叙述尚未更新以匹配更改的代码。我建议恢复到明显符合 FCin 可以给出的建议的早期版本。

标签: c# asynchronous async-await


【解决方案1】:

await 按照它说的做,它在等待。当您点击await 时,它不会转到调用者方法,它会等到Todo 完成。现在为什么要在等待时使用async/await?因为在这个等待期间,它会做其他工作。它使您的 UI 保持运行。它处理来自消息队列的消息,例如单击按钮、绘制控件、计算您的光标是否碰到任何会突出显示控件的边界框等。

您现在的代码是完全同步的,因为您没有在等待Task.Delay。对于每个未等待的方法,您甚至可能会收到警告。

下面是这段代码的执行方式:

  1. 等待 3000 毫秒
  2. 返回“从异步任务返回”
  3. 等待 500 毫秒
  4. 打印“从异步任务返回”
  5. 打印“终止”

代码:

private async void btn1_Click(object sender, EventArgs e)
{
   await TestAsync();
   Console.WriteLine("terminate");
}

private async Task TestAsync()
{
   string str = await Todo();
   await Task.Delay(500);
   Console.WriteLine(str);
}

private async Task<string> Todo()
{
   await Task.Delay(3000);
   return "return from async task";
}

【讨论】:

  • 实际上我已经发布了我在问题中关注的代码,请参阅评论部分 5,它给人的印象是在遇到 await 运算符时,包含 await 运算符的方法被暂停,并且控制权返回给方法的调用者。我想我是在解释这个错误,但我不确定如何..
猜你喜欢
  • 2016-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-12
相关资源
最近更新 更多