同步到异步调度:如何避免死锁? [英] Sync to async dispatch: how can I avoid deadlock?

查看:126
本文介绍了同步到异步调度:如何避免死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个具有同步方法的类,并调用其他一些异步的库方法.因此,我使用Task.Result等待异步操作完成. WPF应用程序以同步方式调用我的方法.这导致死锁.我知道最好的方法是使所有方法异步,但是我的情况要求它们是同步的.另一方面,他们使用其他异步的库.

I'm trying to create a class that has synchronous methods and calls some other library methods which are asynchronous. For that reason I use Task.Result to wait for the async operation to finish. My methods are called by WPF app in synchronous way. This leads to a deadlock. I know that the best way is to make all my methods asynchronous but my situation requires them to be synchronous. From the other hand they use other library which is asynchronous.

我的问题是:在这种情况下如何避免僵局?

My question is: How can I avoid the deadlock in such situation?

复制步骤:

  1. 用户点击应用中的按钮(方法Button1_OnClick)

此方法创建IPlugin的实例,然后调用其方法RequestSomething()

This method creates an instance of IPlugin and then calls its method RequestSomething()

然后该方法以这种方式调用异步库:asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result

This method then calls async library this way: asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result

该库回调其方法NotifyNewValueProgressAsync()

NotifyNewValueProgressAsync()将调用委派给WPF应用程序

NotifyNewValueProgressAsync() delegates the call back to the WPF application

由于UI上下文被此行asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result阻塞,因此步骤5中的回调导致死锁.

Since the UI context is blocked by this line asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result the callback in step 5 leads to a deadlock.

请参见下面的代码示例和相关注释:

See code example below and related comments:

public class SyncAdapterPlugin : IPlugin, IProgressAsyncHandler
{
    //Constructor and fields are omitted here

    //This method is called from UI context by WPF application and it delegates synchronous call to asynchronous method
    string IPlugin.RequestSomething()
    {
        //In order to be able to run the callback I need to capture current UI context
        _context = TaskScheduler.FromCurrentSynchronizationContext();

        var asyncTarget = new ClassFromMyLibrary1(this);
        var resultFromAsyncLibrary = asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result; //Deadlock here!
        return resultFromAsyncLibrary;
    }

    //This method does opposite, it delegates asynchronous callback to synchronous
    async Task<bool> IProgressAsyncHandler.NotifyNewValueProgressAsync(string message)
    {
        //NotifyNewValueProgress method is implemented by WPF application and will update UI elements.
        //That's why it's needed to run the callback on captured UI context.
        Func<bool> work = () => _syncProgressHandler.NotifyNewValueProgress(message);
        if (_context != null)
        {
            return await
                Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.None, _context)
                .ConfigureAwait(false);
        }
        return work();
    }
}

完整的代码示例在此处 https://dotnetfiddle.net/i48sRc .

Complete code example is here https://dotnetfiddle.net/i48sRc.

仅供参考,您可以在推荐答案

插件框架从根本上来说是有缺陷的.特别是,它需要一个 synchronous RequestSomething,它希望能够调用NotifyNewValueProgressAsync来更新UI.但是,当UI线程运行同步方法时,不可能显示UI更新.

The plugin framework is fundamentally flawed. In particular, it requires a synchronous RequestSomething that expects to be able to call NotifyNewValueProgressAsync to update the UI. However, it's not possible to display a UI update while the UI thread is running a synchronous method.

强迫您使用最危险和最危险的异步同步hack之一:嵌套的调度程序框架.这种黑客的主要痛苦在于,它在整个UI层中引入了可重入性,这是最微妙和最困难的一种并发问题.

This forces you to use one of the most dangerous and evil sync-over-async hacks: the nested message loop hack (as briefly described in my article on brownfield async). Since this is a WPF app, you'd use a nested dispatcher frame. The main pain with this hack is that it introduces reentrancy across your entire UI layer, which is the most subtle and difficult kind of concurrency problem.

这篇关于同步到异步调度:如何避免死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆