如果我有一个带有同步方法的应用程序,在UI线程上调用异步方法是否安全,或者是否存在问题或潜在的死锁情况?我知道调用Wait肯定会引起问题,但我觉得这可能会有用。
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
我可以毫无问题地在UI线程上成功运行,但我觉得我可能会遗漏某些东西。我知道我可以使用Task和ContinueWith,但是在这个例子中,我想在退出同步方法之前等待async方法的结果。
更新/澄清
在上面的示例中,我们假设MyMainMethod是重写方法或属性等,并且不能修改为异步。
答案 0 :(得分:7)
让我们来看看你的代码:
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
无论getResultAsync
内发生了什么,此代码在调用task.Result
时都会阻止UI线程。在大多数情况下,这已经错了。
此外,您getResultAsync
为async
的事实表明其中已存在异步操作。除非您在Task.Run
内执行CPU和IO绑定任务的混合,否则没有理由用getResultAsync
包装它。即使这样,也可能没有必要(有关详细信息,请参阅this)。
您可以使用await
控制getResultAsync
内的ConfiureAwait(false)
延续上下文,并且应尽可能避免死锁和冗余上下文切换。
因此,代码可以简化为:
public void MyMainMethod(){
var getResult = getResultAsync().Result;
myLabel.Text = getResult;
}
原样,它仍会阻止UI。为避免阻止,您需要将其设为async
。请参阅Stephen Cleary的Best Practices in Asynchronous Programming Async All as Way 。
如果 无法修改为async
(正如您的问题更新中所阐明的那样),那么以上是您可以获得的最佳效果。 确实,它仍然可能导致死锁,具体取决于getResultAsync
内部的情况,而没有Task.Run
。为避免死锁,您不应尝试使用control.Invoke
内的getResultAsync
同步调用或await
使用TaskScheduler.FromCurrentSynchronizationContext
在UI线程上调度的任何任务来访问UI线程。
但是,通常 可能并且希望将这样的代码重新分解为异步版本:
public async Task MyMainMethod(){
var getResult = await getResultAsync();
myLabel.Text = getResult;
}
您将从应用的顶级入口点调用它,就像UI事件处理程序一样:
async void Button_Click(object sender, EventArg e)
{
try
{
await MyMainMethod();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
答案 1 :(得分:0)
最好通过调度员调用你的ui更新。
Task task = LoadTask();
task.ContinueWith(t =>
Dispatcher.BeginInvoke(() => UpdateUI()));
public async Task LoadTask()
{
Task getdata =
Task.Factory.StartNew(() =>
{
Sleep(3000);
});
await getdata;
return;
}