如何等待任务完成而WinForm中没有死锁,其中开发人员无法向当前方法添加异步

问题描述:

我们有一个WinForm应用程序,它具有所有其他窗体继承的基本窗体。
在基础窗体上有一个Delete按钮,Delete按钮调用一个名为DeleteData的虚拟bool方法。如何等待任务完成而WinForm中没有死锁,其中开发人员无法向当前方法添加异步

public virtual void DeleteButton_Click(object sender, EventArgs e) 
{ 
    if (DeleteData()) 
    { 
     // run some cleanup code 
     DoSomeCleanup(); 
    } 
} 

public virtual bool DeleteData() 
{ 
    throw new NotImplementedException("Not implemented."); 
} 

子窗体已经覆盖了DeleteData方法

public override bool DeleteData() 
{ 
    try 
    { 
     // Delete some data 

     // Call async method that does some UI stuff and wait on it to finish 
     SomeSharedAsyncMethod(); 

     return true; 
    } 
    catch(Exception ex) 
    { 
     // Handle exception 

     return false; 
    } 
} 

这里是收集,SomeSharedAsyncMethod是标记为异步和它里面,它做了一些UI类的东西结合到文本框,并调用此方法别人因此该方法必须保持标记为“异步”

public async Task SomeSharedAsyncMethod() 
{ 
    // Some code that has await and also some code that updates textboxes or other UI controls. 

    await Task.Delay(2000); 
    SomeTextBox.Text = "Some Text"; 
} 

因为基本形式看DeleteData我,我不能“异步”添加到“DeleteData”方法运行“DoSomeCleanup”和“DoSomeCleanup”将在DeleteData完成前调用。
我们还假设我无法将“异步”添加到删除按钮,因为我没有该项目的控制权。
我也不想重写DeleteButton_Click,因为我不想复制位于基本表单DeleteButton_Click内的所有代码。

这里有一些事情我已经尝试:

public override bool DeleteData() 
{ 
    // Delete some data 

    // Call async method that does some UI stuff and wait on it to finish 

    // Causes a deadlock 
    SomeSharedAsyncMethod().Wait(); 

    // RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method. 
    SomeSharedAsyncMethod().RunSynchronously(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    SomeSharedAsyncMethod().ConfigureAwait(false); 

    // Cross-thread operation not valid 
    Task.Run(async() => { await SomeSharedAsyncMethod(); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Run(() => { SomeSharedAsyncMethod(); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Run(async() => { await SomeSharedAsyncMethod().ConfigureAwait(false); }).Wait(); 

    // This will not wait on SomeSharedAsyncMethod to execute 
    Task.Factory.StartNew(() => 
    { 
     SomeSharedAsyncMethod().ConfigureAwait(false); 
    }, Task.Factory.CancellationToken, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).Wait(); 

    return true; 
} 

我们正在寻找获得DeleteData方法来运行其所有的代码,而不是返回,直到所有代码行已完成的一种方式,这包括SomeSharedAsyncMethod 。

+0

看着锁:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement – Sorceri

+1

检查了这一点:https://stackoverflow.com/a/5097066/5779825。希望它有帮助 – oopsdazie

我不认为有一种方法可以在DeleteData内等待SomeSharedAsyncMethod

DeleteData方法占用UI线程。它需要SomeSharedAsyncMethod才能完成以释放UI线程,但SomeSharedAsyncMethodDeleteData之后开始并且它也需要UI线程。换句话说,你需要在UI线程上运行一些东西才能释放它。这是典型的死锁例子。而这是非常重要的,为什么你不应该混合async.Wait()摆在首位。

那么,这可以如何解决?我建议从SomeSharedMethodAsync中删除所有与UI相关的代码,并将.ConfigureAwait(false)添加到其中的所有异步方法调用中。

public override bool DeleteData() 
{ 
    SomeSharedMethodAsync().ConfigureAwait(false).Wait(); 
    UpdateUI(); 
} 

async Task SomeSharedAsyncMethod() 
{ 
    // do all async stuff here, but DON'T update the UI 
    await Task.Delay(2000).ConfigureAwait(false); 
} 

void UpdateUI() 
{ 
    // update the UI here 
    SomeTextBox.Text = "Some Text"; 
} 

当然,这应该只作为最后的手段。使所有以DeleteButton_Click异步开始的方法更受欢迎。

+0

“oopsdazie - above”发送的文章工作并不需要修改其他方法,但我试图让Nito.AsyncEx执行相同的操作。 https://stackoverflow.com/a/5097066/5779825 – goroth

有两个选项我测试,并且都工作。

  1. How would I run an async Task<T> method synchronously?

这是把我的oopsdazie在评论上方。

请查看:stackoverflow.com/a/5097066/5779825。希望它能帮助 - oopsdazie 10月10日在20:35

这是一个从这里重新发布 https://social.msdn.microsoft.com/Forums/en-US/163ef755-ff7b-4ea5-b226-bbe8ef5f4796/is-there-a-pattern-for-calling-an-async-method-synchronously?forum=async

它基本上是一个异步的辅助类,针对其自身的SynchronizationContext工作。

  1. 使用Stehpen克利 - NITO AsyncEx的NuGet
  2. https://github.com/StephenCleary/AsyncEx

    Nito.AsyncEx.AsyncContext.Run(async() => await SomeSharedAsyncMethod()); 
    

    我去与选项2,因为它有很多其他方便的异步助手

开始=>