繁体   English   中英

如何在WinForm上等待开发人员无法在当前方法中添加异步的任务完成而不会死锁

[英]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开头的方法都是异步的是更可取的。

我测试了两个选项,并且都有效。

  1. 如何同步运行异步Task <T>方法?

这是在上面的评论中发送给我的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-方法同步?forum = async

基本上,这是一个异步帮助程序类,可针对其自己的SynchronizationContext工作。

  1. 使用Stehpen Cleary-Nito AsyncEx NuGet

https://github.com/StephenCleary/AsyncEx

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

我选择了选项2,因为它还有很多其他方便的异步帮助器

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM