![](/img/trans.png)
[英]wait for a Task that calls an async method to complete without blocking thread
[英]How to wait on a Task to complete without deadlock in a WinForm where developer can't add async to the current method
我们有一个WinForm应用程序,该应用程序具有所有其他表单都继承自的基本表单。
在基本表单上,有一个Delete按钮,而Delete按钮调用一个称为DeleteData的虚拟布尔方法。
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”,并且在DeleteData完成之前将调用“ DoSomeCleanup”。
我们还假设我无法将“异步”添加到删除按钮,因为我无法控制该项目。
我也不想覆盖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相关的代码, SomeSharedMethodAsync
.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的。
检查一下:stackoverflow.com/a/5097066/5779825。 希望对您有所帮助– oopsdazie 10月10日20:35
基本上,这是一个异步帮助程序类,可针对其自己的SynchronizationContext工作。
https://github.com/StephenCleary/AsyncEx
Nito.AsyncEx.AsyncContext.Run(async () => await SomeSharedAsyncMethod());
我选择了选项2,因为它还有很多其他方便的异步帮助器
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.