如何等待任务完成而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 。
我不认为有一种方法可以在DeleteData
内等待SomeSharedAsyncMethod
。
DeleteData
方法占用UI线程。它需要SomeSharedAsyncMethod
才能完成以释放UI线程,但SomeSharedAsyncMethod
在DeleteData
之后开始并且它也需要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异步开始的方法更受欢迎。
“oopsdazie - above”发送的文章工作并不需要修改其他方法,但我试图让Nito.AsyncEx执行相同的操作。 https://stackoverflow.com/a/5097066/5779825 – goroth
有两个选项我测试,并且都工作。
这是把我的oopsdazie在评论上方。
请查看:stackoverflow.com/a/5097066/5779825。希望它能帮助 - oopsdazie 10月10日在20:35
它基本上是一个异步的辅助类,针对其自身的SynchronizationContext工作。
- 使用Stehpen克利 - NITO AsyncEx的NuGet
https://github.com/StephenCleary/AsyncEx
Nito.AsyncEx.AsyncContext.Run(async() => await SomeSharedAsyncMethod());
我去与选项2,因为它有很多其他方便的异步助手
看着锁:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement – Sorceri
检查了这一点:https://stackoverflow.com/a/5097066/5779825。希望它有帮助 – oopsdazie