C# 在UI上的同步方法中运行异步委托是否安全?

C# 在UI上的同步方法中运行异步委托是否安全?,c#,async-await,C#,Async Await,如果我有一个使用同步方法的应用程序,在UI线程上调用如下所示的异步方法是否安全,或者是否存在问题或潜在的死锁情况?我知道呼叫等待显然会引起问题,但我觉得这可能会解决问题 public void MyMainMethod(){ var getResult = Task.Run(async () => { await getResultAsync(); }).Result; myLabel.Text = getResult; } 我可以成功地在UI线程上运行而不会出现问题,但我觉得我

如果我有一个使用同步方法的应用程序,在UI线程上调用如下所示的异步方法是否安全,或者是否存在问题或潜在的死锁情况?我知道呼叫等待显然会引起问题,但我觉得这可能会解决问题

public void MyMainMethod(){
  var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
  myLabel.Text = getResult;
}
我可以成功地在UI线程上运行而不会出现问题,但我觉得我可能遗漏了一些东西。我知道我可以使用Task并继续,但在本例中,我希望在退出同步方法之前等待async方法的结果

更新/澄清


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

最好通过dispatcher调用ui更新

 Task task = LoadTask();

 task.ContinueWith(t => 
          Dispatcher.BeginInvoke(() => UpdateUI()));



    public async Task LoadTask() 
    {         
       Task getdata =                
             Task.Factory.StartNew(() =>                
        {
             Sleep(3000);

        });
        await getdata;
        return;
    }

让我们看看您的代码:

public void MyMainMethod(){
  var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
  myLabel.Text = getResult;
}
无论
getResultAsync
内部发生了什么,当调用
task.Result
时,此代码都会阻塞UI线程。在大多数情况下,这已经是错误的

此外,您的
getResultAsync
async
这一事实表明它内部已经有了一个异步操作。没有理由用
Task.Run
来包装它,除非在
getResultAsync
中混合执行CPU和IO绑定的任务。即使如此,也可能没有必要(有关更多详细信息,请参阅)

您可以使用
confireawait(false)
控制
wait
中的
getResultAsync
继续上下文,并应尽可能避免死锁和冗余上下文切换

因此,代码可以简化为:

public void MyMainMethod(){
  var getResult = getResultAsync().Result;
  myLabel.Text = getResult;
}
它仍然会阻塞UI。为了避免阻塞,您需要使其
异步
。请参阅Stephen Cleary的《异步》一路走来

如果无法将其修改为
async
(如问题更新中所述),则上述内容是您所能获得的最好结果事实上,它仍然可能导致死锁,具体取决于
getResultAsync
内部的情况,而不执行
任务。运行
。为避免死锁,您不应尝试使用同步调用(如
control.Invoke
inside
getResultAsync
)访问UI线程,或
wait
使用
TaskScheduler.FromCurrentSynchronizationContext
在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);
    }
}

谢谢你的建议。在大多数情况下,我理解这是可能要做的,但在本例中,我希望在存在同步方法之前等待异步方法完成。我更新了上面的示例。“此外,getResultAsync是异步的这一事实表明它内部已经有一个异步操作。没有理由用Task.Run来包装它”-只有正确实现了
getResultAsync
,这才是准确的。如果它是一个
async
方法,其中包含
await
语句,而不包含
ConfigureAwait(false)
,则在
TaskScheduler上启动
getResultsAsync
。默认的
和取消包装(据我所知,
Task.Run
就是这样做的)可能会使您免于死锁。否则答案很好。@KirillShlenskiy,这是一个很好的观点,我链接以获取更多详细信息确实使用了
ConfigureAwait(false)
。OTOH,如果它被称为
Task.Run(lambda).Result
并且在
lambda
中的任何地方,他执行类似于
control.Invoke的操作,就会出现死锁。总之,这取决于
getResultsAsync
中的内容。我真的很惊讶这只有5张赞成票。。。这是我见过的最好的解释之一。谢谢我不同意你的澄清。有办法让<代码>异步代码>代码工作,你只需要退后一步,考虑一下。如果方法实现是
async
,则基本签名应返回
任务
。我有和的博客条目。异步代码上的同步阻塞不应该是必须的;几乎总是有更好的解决方案。