我试图创建一个具有同步方法的类,并调用其他一些异步的库方法。出于这个原因,我使用Task.Result
等待异步操作完成。我的方法由WPF app以同步方式调用。这导致僵局。我知道最好的方法是使我的所有方法都异步,但我的情况要求它们是同步的。另一方面,他们使用其他异步库。
我的问题是:如何在这种情况下避免陷入僵局?
重现的步骤:
用户点击应用中的按钮(方法Button1_OnClick
)
此方法创建IPlugin
的实例,然后调用其方法RequestSomething()
然后,此方法以这种方式调用异步库:asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result
该库回调其方法NotifyNewValueProgressAsync()
NotifyNewValueProgressAsync()
将回调委托给WPF应用程序
由于此行asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result
阻止了UI上下文,因此步骤5中的回调会导致死锁。
请参阅下面的代码示例和相关注释:
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。
仅供参考,有关此问题的一些背景信息,您也可以在this SO question找到。
答案 0 :(得分:1)
插件框架存在根本缺陷。特别是,它需要同步 RequestSomething
,希望能够调用NotifyNewValueProgressAsync
来更新UI。但是,当UI线程运行同步方法时,无法显示UI更新。
这个强制你使用最危险和最邪恶的同步异步黑客攻击之一:nested message loop hack(正如我在blackfield async上的文章中简要描述的那样)。由于这是一个WPF应用,因此您使用nested dispatcher frame。这个黑客的主要痛苦在于它在整个UI层引入了重入,这是最微妙和最困难的并发问题。
答案 1 :(得分:0)
根据定义,同步方法不会是异步的。您将需要在使用TAP的Task中从UI中调用同步方法,并在等待异步时等待它们。