java 同步锁 异步锁,同步到异步调度:如何避免死锁?

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?

Steps to reproduce:

User hits a button in the app (method Button1_OnClick)

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

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

The library calls back its method NotifyNewValueProgressAsync()

NotifyNewValueProgressAsync() delegates the call back to the WPF application

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 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 work = () => _syncProgressHandler.NotifyNewValueProgress(message);

if (_context != null)

{

return await

Task.Factory.StartNew(work, CancellationToken.None, TaskCreationOptions.None, _context)

.ConfigureAwait(false);

}

return work();

}

}

FYI, Some background on this issue you can also find in this SO question.

解决方案

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.

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值