我对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方法为止。好的,但是不能,可以吗?如果在其他情况下,甚至没有任何有意义的虚空方法可以爬上去,怎么办?
答案 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;
}