可以安全地在UI上的同步方法中运行异步委托吗?

时间:2014-01-28 01:55:27

标签: c# async-await

如果我有一个带有同步方法的应用程序,在UI线程上调用异步方法是否安全,或者是否存在问题或潜在的死锁情况?我知道调用Wait肯定会引起问题,但我觉得这可能会有用。

public void MyMainMethod(){
  var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
  myLabel.Text = getResult;
}

我可以毫无问题地在UI线程上成功运行,但我觉得我可能会遗漏某些东西。我知道我可以使用Task和ContinueWith,但是在这个例子中,我想在退出同步方法之前等待async方法的结果。

更新/澄清

在上面的示例中,我们假设MyMainMethod是重写方法或属性等,并且不能修改为异步。

2 个答案:

答案 0 :(得分:7)

让我们来看看你的代码:

public void MyMainMethod(){
  var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
  myLabel.Text = getResult;
}

无论getResultAsync内发生了什么,此代码在调用task.Result时都会阻止UI线程。在大多数情况下,这已经错了。

此外,您getResultAsyncasync的事实表明其中已存在异步操作。除非您在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;
    }
相关问题