C#中的异步与等待:我是否必须将整个调用链更改为无效方法?

时间:2018-06-21 07:50:30

标签: c# multithreading asynchronous async-await

我对C#上的整个异步/等待主题完全陌生。尽管回答了无数问题,并获得了教程being linked to relentlessly的帮助,但我似乎仍然无法全神贯注于如何使用异步/等待。我要实现的是wpf应用程序在等待昂贵的方法调用完成时返回到UI上的渲染动画-在我的以下示例中,将为GetLinesAsync(),或更具体地说,为{{1 }}内:

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); } 不需要花很长时间来执行,应用程序也会完全冻结。除此之外,我不确定为什么会发生这种情况,这是我从阅读有关await / async的教程和解释中了解到的,特别是那部分说您只能等待返回void或Task的方法:

expensive_function()行实际上应该说foreach(string s in GetLinesAsync().Result)-但是我必须将foreach(string s in await GetLinesAsync().Result)标记为异步,然后我必须将GetMessages()替换为{{ 1}},并将MessageBox.Show(GetAMessage())标记为异步。

换句话说:可等待性将爬到调用堆栈上,直到到达void方法为止。好的,但是不能,可以吗?如果在其他情况下,甚至没有任何有意义的虚空方法可以爬上去,怎么办?

1 个答案:

答案 0 :(得分:5)

您的代码被阻止的原因是因为您在此处对其进行了阻止。

foreach(string s in GetLinesAsync().Result)

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

只需让asyn c自由流动即可。

  • 在Windows Forms和WPF中,async / await具有方便的属性,可以在您等待的异步操作完成后返回到UI线程

  • 在void方法上对async的支持是专门为支持事件处理程序而添加的。但是请确保您能够处理异常,因为无效将不会被观察到

所以您可以这样做。

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

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;
}