在C#中异步和等待:我是否必须将整个调用链更改为Void方法?

dra*_*gen 0 c# multithreading asynchronous async-await

我对C#上的整个Async/Await主题完全不熟悉.尽管有无数的问题得到解答,并且教程被无情地链接起来,但我仍然无法理解如何使用async/await.我想要实现的是wpf应用程序返回到UI上的渲染动画,同时它等待一个昂贵的方法调用来完成 - 在我的下面的例子中,那是GetLinesAsync()或者更具体地说,expensive_function()在:

void Button_Click(object sender, RoutedEventArgs e) {
    MessageBox.Show(GetAMessage());
}

string GetAMessage() {
    string ret = "";
    foreach(string s in GetLinesAsync().Result)
        ret += $"{s}\n";
    return ret;
}

async Task<List<string>> GetLinesAsync() {
    List<string> ret = new List<string>();
    string[] ExpensiveResult = null;
    if (await Task.Run(() => expensive_function(ref ExpensiveResult)) == expensive_stat.SUCCESS && ExpensiveResult != null)
    {
        foreach(string s in ExpensiveResult)
            ret.Add(s);
    }
Run Code Online (Sandbox Code Playgroud)

这样写,应用程序完全冻结,即使expensive_function()执行时间不长.除此之外,我不确定为什么会发生这种情况,这是我从await/async上阅读教程和解释时所理解的,特别是那些说你只能等待返回void或Task的方法的部分:

该生产线foreach(string s in GetLinesAsync().Result)实际上应该说foreach(string s in await GetLinesAsync().Result)-但后来我不得不纪念GetMessages()异步,然后我不得不更换MessageBox.Show(GetAMessage())MessageBox.Show(await GetMessages())和标记Button_Click()异步.

换句话说:awaitability会爬上调用堆栈,直到它达到void方法.好的,但那不可能,不是吗?如果在某些其他情况下,甚至没有可感知的无效方法可以实现呢?

AAA*_*ddd 5

你编码阻塞的原因是你在这里阻止它.

foreach(string s in GetLinesAsync().Result)
Run Code Online (Sandbox Code Playgroud)

你说UI线程等到我的昂贵任务完成

async流畅.

  • 在Windows窗体和WPF中,async/ await具有在您等待的异步操作完成时返回UI线程的便利属性

  • async特别添加了对void方法的支持以支持事件处理程序.但是请确保您处理异常,因为无法观察到void

所以你可以这样做.

注意:这是一个基于您的代码的极其简化和消毒的示例

public async void Button_Click(object sender, RoutedEventArgs e)
{
   try
   {
      MessageBox.Show(await GetAMessage());
   }
   catch (Exception exception)
   {
      // log the nelly out of it
      // or display message
   }

}

public async Task<string> GetAMessage()
{
   var list = await GetLinesAsync();

   return string.Join("\n", list);
}

public List<string> ExpensiveFunction()
{
   // ...
   return new List<string>();
}


public async Task<List<string>> GetLinesAsync()
{
   var result = Task.Run(() => ExpensiveFunction());
   return await result;
}
Run Code Online (Sandbox Code Playgroud)