async和await在哪里结束?混乱

本文关键字:混乱 结束 在哪里 await async | 更新日期: 2023-09-27 18:17:21

我有一个程序,没有目的,但帮助我了解async和await是如何工作的。它是一个控制台应用程序,解析XML并等待返回一个名称,可以是姓,也可以是名。下面是代码:

static void Main(string[] args)
{
     Task<string> name = GetFirstParsedName();
     name.Wait();
     if (name.IsCompleted)
     {
         Console.WriteLine(name.Result);
     }
     Console.ReadLine();
 }
static async Task<string> GetFirstParsedName()
{
  string xmlSnippet = @"<person>
<FirstName>Chamir</FirstName>
<Surname>Bodasing</Surname>
<Gender>Male</Gender>
<Nationality>South African</Nationality></person>";
  XmlDocument xmlDoc = new XmlDocument();
  xmlDoc.LoadXml(xmlSnippet);
  XmlParser xmlParser = new XmlParser();
  Task<string> t_GetFirstName = xmlParser.GetFirstName(xmlDoc);
  Task<string> t_GetSurname = xmlParser.GetSurname(xmlDoc);  
  Task<string> t_firstReturnedName = await Task.WhenAny(new Task<string>[] { t_GetFirstName, t_GetSurname });
  string firstReturnedName = await t_firstReturnedName;
  return firstReturnedName;    
}
static async Task<string> GetFirstName(XmlDocument personXml)
{
  string firstName = personXml.SelectSingleNode("//FirstName").InnerText;
  await Task.Delay(5000);
  return firstName;
}
static async Task<string> GetSurname(XmlDocument personXml)
{
  string surname = personXml.SelectSingleNode("//Surname").InnerText;
  await Task.Delay(1);
  return surname;
}

似乎只有在不需要向主方法返回值时才有意义。除非它意味着设置一个全局的类属性,这样就可以被访问。如果不是,为了等待方法,所有方法都需要是异步的,这反过来意味着返回类型必须是Task<T>。它似乎永远不会结束,除非我显式地编写以下代码(如上面的主方法):

Task<string> name = GetFirstParsedName();
name.Wait();
if (name.IsCompleted)
{
    Console.WriteLine(name.Result);
 }

我的理解正确吗?我必须使用结果属性来获得这里的值,从阅读关于这一点,似乎这不是最佳实践。

async和await在哪里结束?混乱

似乎只有在不需要向主方法返回值时才有意义。除非它意味着要设置一个全局的类属性,这样就可以被访问了。

你的async方法可以返回Task<T>返回一个值给它的调用者。如果异步方法依赖于副作用(即设置属性/全局变量),它们就不能很好地工作;如果你的代码更纯粹(即接受参数并返回结果),它们会工作得更好。

如果不是,为了等待方法,所有方法都需要是异步的,这反过来意味着返回类型必须是"Task"。似乎永远不会结束

这就是为什么async的中心原则之一是"异步所有的方式"。在大多数应用程序中,这正是您应该做的。最终,"异步链"通常以async void事件处理程序(用于UI应用程序)或async Task<T>入口点(用于ASP)结束。网络应用程序)。主机应用程序是不寻常的,因为它们确实需要显式的Wait()/Result或等效的Main方法。

毕竟,async的全部意义在于释放调用线程。如果调用堆栈上的下一个方法阻塞了同一个线程,直到async代码完成,那么,这是一大堆没有任何好处的工作…

似乎只有在不需要向主方法返回值时才有意义。

你为什么这么说?在进行自然异步操作时,使用异步方法是有意义的。不管该操作是否有返回值

为了等待方法,所有方法都需要是异步的,这意味着返回类型必须是"Task"。似乎永远不会结束

这是正确的。Async在你的代码中像一块木板一样,从栈的底部延伸到栈的顶部。它通常到达堆栈中的最高调用位置(无论是控制台Main方法还是UI事件处理程序)。这就是使用async的优点,它允许您在释放调用线程的同时异步等待操作。例如,如果您有一个需要并发处理大量请求的WebAPI端点,这将有所帮助。如果你把大部分时间花在查询数据库上,你可以同时释放调用线程来处理更多的请求。

我的理解正确吗?我必须使用结果属性来获得这里的值,从阅读这一点来看,这似乎不是最佳实践。

您必须使用Result属性,因为控制台应用程序是一种特殊情况,Main不能被标记为async(除非您使用ASP。. NET CoreCLR控制台应用程序)。如果这是一个UI事件处理程序或ASP。. NET操作,您将正确地await异步调用

好吧,async关键字让编译器知道你的方法将执行一系列异步调用,这些调用应该最有可能等待,但不应该阻塞main(或UI)线程。

为了帮助你理解async-await,这里有一些简单的例子:

考虑这个场景,当用户在WinForm的应用程序上点击"保存"按钮时,一些UI操作在不同的线程中异步启动。

代码是:

    private Task SomeUIOperation()
    {
        // ui operation
        return Task.Run(() =>
        {
            this.Invoke(new Action(() => this.BackColor = Color.Aquamarine));
            Thread.Sleep(10000);
            this.Invoke(new Action(() => this.BackColor = Color.Gray));
        });
    }
    private async void button1_Click(object sender, EventArgs e)
    {
        await SomeUIOperation();
        // some other stuff
    }

如果我们在这里不使用async-await, UI线程将在10秒内无响应。

这是你通常如何使用async-await的一个例子,当你想要一些代码只在异步操作完成时才执行,同时你不希望主线程被阻塞时,你可以使用它。

控制台应用程序不是测试和学习Async-Await的最佳项目类型