【问题标题】:Where does async and await end? Confusionasync 和 await 在哪里结束?混乱
【发布时间】:2016-10-02 18:15: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;
}

似乎只有当您不必将值返回给 main 方法时才有意义使用 async 方法。除非这意味着设置一个可以访问的全局类属性。如果不是,为了等待该方法,所有方法都需要是异步的,这反过来意味着返回类型必须是Task&lt;T&gt;。除非我明确地必须编写以下代码(如上面的 main 方法),否则它似乎永远不会结束:

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

我的理解完全正确吗?我必须使用 result 属性来获取此处的值,并且通过阅读有关此内容,这似乎不是最佳做法。

【问题讨论】:

标签: c# asynchronous async-await task-parallel-library


【解决方案1】:

似乎只有在您不必将值返回给 main 方法时才使用 async 方法才有意义。除非这意味着设置一个可以访问的全局类属性。

您的async 方法可以返回Task&lt;T&gt; 以向其调用者返回一个值。如果异步方法依赖于副作用(即设置属性/全局),则它们不能很好地工作;如果您的代码更纯粹(即获取参数并返回结果),它们会更好地工作。

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

这就是为什么async 的核心原则之一是“一路异步”。在大多数应用程序中,这正是您应该做的。最终,“异步链”通常以async void 事件处理程序(用于 UI 应用程序)或async Task&lt;T&gt; 入口点(用于 ASP.NET 应用程序)结束。控制台应用程序很不寻常,因为它们在 Main 方法中确实需要明确的 Wait()/Result 或等效项。

毕竟,async 的全部意义在于释放调用线程。如果调用堆栈中的下一个方法阻塞同一线程,直到async 代码完成,那么,这就是一大堆无益的工作......

【讨论】:

  • 感谢您指出“一路异步”。在牢记这一原则的同时更容易理解。
【解决方案2】:

似乎只有当您不必将值返回给 main 方法时才使用 async 方法。

你为什么这么说?使用异步方法是有意义的——只要你有一个自然的异步操作正在进行。无论该操作是否有返回值。

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

没错。来自bottom to the top of your stack 的异步在您的代码中传播,就像一个 plage。它通常会到达堆栈中的最高调用位置(无论是控制台 Main 方法还是 UI 事件处理程序)。这就是使用async 的优势,它允许您在释放调用线程的同时异步等待操作。例如,如果您有一个需要同时处理大量请求的 WebAPI 端点,这会有所帮助。如果您将大部分时间花在查询数据库上,则可以同时释放该调用线程以处理更多请求。

我的理解完全正确吗?我必须使用 result 属性来获取此处的值,并且通过阅读有关此内容,这似乎不是最佳做法。

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

【讨论】:

  • 啊,所以它只是控制台很特别。所以这意味着如果我有一个 MVC 控制器,我可以将 async 修饰符添加到方法签名中,并在正常异步条件下使用它,而不是在控制台特殊情况下使用它?我现在要制作一个演示。
  • @downvoter 所以请阐明我的回答有什么问题。
【解决方案3】:

好吧,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 的最佳项目类型

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多