C# 异步和等待在哪里结束?混乱

C# 异步和等待在哪里结束?混乱,c#,asynchronous,async-await,task-parallel-library,C#,Asynchronous,Async Await,Task Parallel Library,我有一个程序,它除了帮助我理解async和await是如何工作的外,没有任何目的。它是一个控制台应用程序,解析XML并等待返回一个名字,或者是姓氏,或者是名字。代码如下: static void Main(string[] args) { Task<string> name = GetFirstParsedName(); name.Wait(); if (name.IsCompleted) { Console.WriteLin

我有一个程序,它除了帮助我理解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;
}
static void Main(字符串[]args)
{
任务名称=GetFirstParsedName();
name.Wait();
如果(name.IsCompleted)
{
Console.WriteLine(name.Result);
}
Console.ReadLine();
}
静态异步任务GetFirstParsedName()
{
字符串xmlSnippet=@”
查米尔
博达斯
男性
南非”;
XmlDocument xmlDoc=新的XmlDocument();
LoadXml(xmlsippet);
XmlParser XmlParser=新的XmlParser();
Task t_GetFirstName=xmlParser.GetFirstName(xmlDoc);
Task t_getnamese=xmlParser.getnamese(xmlDoc);
Task t_firstReturnedName=wait Task.WhenAny(新任务[]{t_GetFirstName,t_getname});
string firstReturnedName=等待t_firstReturnedName;
returnfirstreturnedname;
}
静态异步任务GetFirstName(XmlDocument personXml)
{
字符串firstName=personXml.SelectSingleNode(//firstName”).InnerText;
等待任务。延迟(5000);
返回名字;
}
静态异步任务getNames(XmlDocument personXml)
{
字符串姓氏=personXml.SelectSingleNode(//姓氏”).InnerText;
等待任务。延迟(1);
返回姓氏;
}
似乎只有在不必向主方法返回值的情况下,才有必要使用async方法。除非它意味着设置一个可以访问的全局类属性。如果不是,为了等待该方法,所有方法都需要异步,这意味着返回类型必须是
Task
。它似乎永远不会结束,除非我显式地编写以下代码(如上面的主方法所示):

Task name=GetFirstParsedName();
name.Wait();
如果(name.IsCompleted)
{
Console.WriteLine(name.Result);
}
我的理解完全正确吗?我必须使用result属性来获取这里的值,从阅读中,这似乎不是最佳实践

似乎只有在不必向主方法返回值的情况下,才有必要使用async方法

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

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

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

我的理解完全正确吗?我必须使用result属性来获取这里的值,从阅读中,这似乎不是最佳实践


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

好吧,
async
关键字让编译器知道您的方法将执行一系列异步调用,这些调用很可能会被等待,但不应该阻止
主线程

为了帮助您理解async Wait,下面是一些简单的示例:

考虑这个场景,当用户单击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
    }
如果我们不在这里使用
异步等待
,UI线程将在10秒内无响应

这是一个通常使用async await的示例,当您希望某段代码仅在异步操作完成时执行,同时又不希望主线程被阻塞时,您可以使用它

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

似乎只有在不必向主方法返回值的情况下,才有必要使用async方法。除非它意味着设置一个可以访问的全局类属性

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

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

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


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

我相信OP理解async的功能。是控制台造成了混乱。
    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
    }