在同步异步避免死锁,并防止响应UI(Sync over Async avoiding deadloc

2019-07-19 17:54发布

我们有一个正在使用的WPF和/或客户的WinForms库。

我们已经提供了类似的异步方法:

Task<int> GetIntAsync()

我们还(不幸)提供的同步封装方法:

int GetInt();

基本上只是调用异步方法,并调用.Result上了它的任务。

最近,我们在某些情况下在某些代码实现GetIntAsync需要在主UI线程上运行(它需要使用标记为“单次”线程模型的传统COM组件(即组件必须在主STA线程上运行而不只是任何STA线程)

所以问题是,当GetInt()被称为主线程,它会因为死锁

  • .Result块为主线,
  • 中的代码GetIntAsync()使用Dispatcher.Invoke试图在主线程上运行。

同步方法已经被消耗掉,因此将是一个重大更改,将其删除。 因此,相反,我们选择了使用WaitWithPumping在我们的同步GetInt()方法,以允许调用主线程工作。

这个工程除了使用客户精细GetInt()从他们的UI代码。 此前,他们预计使用GetInt()将离开自己的用户界面反应迟钝-也就是说,如果他们叫GetInt()从按钮的单击事件处理程序中,他们会认为没有窗户的消息进行处理,直至处理程序返回。 现在,消息被泵送,他们的UI 响应和相同的按钮可以再次点击(他们可能没有编写自己的处理程序是重入)。

如果有一个合理的解决方案,我们希望不会有我们的客户需要在通话过程中的代码对UI响应GetInt

题:

  • 有没有办法做一个WaitWithPumping将泵“援引主”的消息,但不抽其他UI相关的消息?
  • 这将满足我们的要求,如果客户端UI的表现好像目前均呈现出模态对话框,虽然隐藏(即用户无法访问其他窗口)。 但是,从我读,你不能隐藏一个模式对话框。
  • 你能想到的其他解决方法,将不胜感激。

Answer 1:

而不是利用现有的消息泵可以的范围内创建自己的消息泵GetInt 。 这里是一个博客条目讨论如何写一个。 这是一个完整的解决方案的博客创建 。

使用,你可以把它写成:

public int GetInt()
{
    return AsyncPump.Run(() => GetIntAsync());
}

这将导致彻底阻断预期UI线程,同时还要确保所有从名为延续GetIntAsync不死锁,因为他们会被编排到一个不同SynchronizationContext 。 另外请注意,此消息泵仍然是主要的STA / UI线程上运行。



文章来源: Sync over Async avoiding deadlock and prevent UI from being responsive