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

str*_*per 5 c# asynchronous task-parallel-library 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Task<string> name = GetFirstParsedName();
name.Wait();
if (name.IsCompleted)
{
    Console.WriteLine(name.Result);
 }
Run Code Online (Sandbox Code Playgroud)

我的理解是否正确?我必须使用result属性来获取这里的值,并且通过阅读这个,似乎这不是最好的做法.

Ste*_*ary 8

当您不必将值返回到main方法时,似乎只有使用异步方法才有意义.除非它意味着设置一个可以访问的全局类属性.

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

如果没有,为了等待该方法,所有方法都需要是异步的,这反过来意味着返回类型必须是"任务".好像它永远不会结束

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

毕竟,重点async是释放调用线程.如果调用堆栈的下一个方法阻塞了相同的线程,直到async代码完成,那么,这是一大堆工作,没有任何好处......


Yuv*_*kov 5

当您不必将值返回到main方法时,似乎只有使用异步方法才有意义.

你为什么这么说?使用异步方法是有意义的 - 在这种方法中,您正在进行自然异步操作.无论该操作是否具有返回值或不具有返回值.

为了等待该方法,所有方法都需要是异步的,这反过来意味着返回类型必须是"任务".好像它永远不会结束

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

我的理解是否正确?我必须使用result属性来获取这里的值,并且通过阅读这个,似乎这不是最好的做法.

您必须使用该Result属性,因为控制台应用程序是一种特殊情况,Main无法标记为async(除非您使用的是ASP.NET CoreCLR控制台应用程序).如果这是一个UI事件处理程序或ASP.NET操作,那么您就可以正确await执行异步调用.