2013-02-11 83 views
6

我们有一个WPF和/或Winforms客户端正在使用的库。通过异步同步避免死锁并防止UI响应

我们已经提供了类似的异步方法:我们也(不幸)提供的同步封装方法

Task<int> GetIntAsync() 

int GetInt(); 

基本上只是调用异步方法和调用其任务是.Result

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

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

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

同步方法已被占用,因此将其删除将是一个突破性更改。相反,我们选择在我们的同步GetInt()方法中使用WaitWithPumping以允许调用主线程来工作。

这个工作正常,除了从他们的UI代码中使用GetInt()的客户端。以前,他们预计使用GetInt()会使其UI停止响应 - 也就是说,如果他们从按钮的单击事件处理程序中调用GetInt(),则他们会希望在处理程序返回之前不会处理任何Windows消息。现在消息被抽出,他们的UI 响应,同样的按钮可以再次点击(他们可能没有编写他们的处理程序是可重入的)。

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

问:

  • 是否有方法做WaitWithPumping,将泵“调用主”消息,但不泵其他UI相关的消息?
  • 如果客户端用户界面的行为与当前显示的模式对话框一样,尽管处于隐藏状态(即用户无法访问其他窗口),那就足够了。但是,从我阅读的内容来看,您无法隐藏模式对话框。
  • 你可以想到其他解决方法,将不胜感激。

回答

4

不使用现有的消息泵,您可以在GetInt的上下文中创建自己的消息泵。 Here是一个博客文章,讨论如何写一个。 This is the full solution the blog creates

使用,你可以把它写成:

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

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

+0

谢谢。我确实看到了UI块。不幸的是,试图进入主线程的代码使用主线程的Dispatcher Dispatcher.Invoke来进入主线程。我猜想做这个工作,我需要将该代码更改为使用SynchronizationContext,而不是主线程的Dispatcher。有什么办法可以使它与主线程的Dispatcher一起工作吗? – 2013-02-12 17:33:28

+0

@MattSmith你的代码没有特别的工作,因为你正在使用主线程的调度程序。 – Servy 2013-02-12 18:28:42