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

我正在尝试创建一个具有同步方法的类,并调用其他一些异步的库方法。 出于这个原因,我使用Task.Result等待异步操作完成。 我的方法由WPF app以同步方式调用。 这导致僵局。 我知道最好的方法是使我的所有方法都异步,但我的情况要求它们是同步的。 另一方面,他们使用异步的其他库。

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

重现步骤:

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

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

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

  4. 该库调用它的方法NotifyNewValueProgressAsync()

  5. NotifyNewValueProgressAsync()将调用委托给WPF应用程序

  6. 由于UI上下文被此行阻止了asyncTarget.MethodFromAsyncLibrary("HelloFromPlugin").Result步骤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 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(); } } 

完整的代码示例在这里https://dotnetfiddle.net/i48sRc 。

仅供参考,关于这个问题的一些背景,你也可以在这个问题中找到。

插件框架存在根本缺陷。 特别是,它需要一个同步的 RequestSomething ,它希望能够调用NotifyNewValueProgressAsync来更新UI。 但是,在UI线程运行同步方法时,无法显示UI更新。

迫使你使用最危险和最邪恶的同步异步黑客之一: 嵌套的消息循环黑客 (正如我在关于brownfield异步的文章中简要描述的那样)。 由于这是一个WPF应用程序,因此您将使用嵌套的调度程序框架 。 这种黑客的主要痛苦在于它在整个UI层引入了重入,这是最微妙和最困难的并发问题。

根据定义,同步方法不会是异步的。 您将需要在使用TAP的Task中从UI中调用同步方法,并等待它们,同时使您从异步中等待的方法。