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

我们提供了一种类似于以下内容的异步方法:

Task<int> GetIntAsync()

我们(不幸的是)还提供了一个同步包装器方法:
int GetInt();

它实际上只是调用异步方法,并在其任务上调用.Result

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

因此,问题在于,在主线程上调用GetInt()时,它将导致死锁,因为
  • .Result阻止主线程
  • GetIntAsync()中的代码使用Dispatcher.Invoke尝试在主线程上运行。

  • 同步方法已经被使用,因此删除它是一项重大更改。因此,相反,我们选择在同步GetInt()方法中使用WaitWithPumping来允许对主线程的调用工作。

    除了使用从其UI代码使用GetInt()的客户端以外,此方法均能正常工作。以前,他们希望使用GetInt()会使用户界面无响应-也就是说,如果他们从按钮的click事件处理程序中调用GetInt(),则他们希望在处理程序返回之前不会处理任何Windows消息。现在,已经发送了消息,它们的UI 响应式的,并且可以再次单击相同的按钮(它们可能没有将其处理程序编码为可重入)。

    如果有合理的解决方案,我们不想让我们的客户需要编写代码,以防止在调用GetInt时UI会响应

    问题:
  • 是否有一种方法可以生成WaitWithPumping来泵送“Invoke to main”消息,但不泵送其他与UI相关的消息?
  • 如果客户端UI的行为就像是当前显示了一个模式对话框(尽管已隐藏)(即用户无法访问其他窗口),就我们的目的而言就足够了。但是,据我所读,您无法隐藏模式对话框。
  • 您可以想到的其他解决方法将不胜感激。
  • 最佳答案

    无需利用现有的消息泵,而是可以在GetInt的上下文中创建自己的消息泵。 Here是讨论如何编写一个博客条目。 This is the full solution the blog creates

    使用它,您可以将其编写为:

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

    这将导致完全按预期方式阻塞UI线程,同时仍确保从GetIntAsync调用的所有连续都不会死锁,因为它们将被编码(marshal)到另一个SynchronizationContext。还请注意,此消息泵仍在STA/UI主线程上运行。

    关于c# - 通过异步同步避免死锁并防止UI响应,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14820606/

    10-12 02:48