c# - 通过异步同步避免死锁并防止 UI 响应

标签 c# wpf winforms task-parallel-library

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

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

Task<int> GetIntAsync()

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

int GetInt();

本质上只是调用异步方法并在其任务上调用 .Result

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

所以问题是当在主线程上调用GetInt()时,它会死锁因为

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

同步方法已经被使用,所以删除它是一个破坏性的改变。因此,我们选择使用 WaitWithPumping在我们的同步 GetInt() 方法中允许调用主线程。

除了从其 UI 代码使用 GetInt() 的客户端外,这工作正常。以前,他们预计使用 GetInt() 会使他们的 UI 无响应——也就是说,如果他们从按钮的点击事件处理程序中调用 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 调用的所有延续不会死锁,因为它们将被编码到不同的 SynchronizationContext。另请注意,此消息泵仍在主 STA/UI 线程上运行。

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

相关文章:

c# - Entity Framework 确保对象按顺序插入

c# - 我可以创建一个包装器来拦截对实现特定接口(interface)的对象的所有调用吗?

c# - ListView ComputedVerticalScrollBarVisibilityProperty 总是返回 Visible?

c# - 域实体中的外键属性

c# - 将字典对象添加到对象列表

wpf - 将项目添加到 ListView 时触发的事件?

C# WPF 非常简单的文本框验证

c# - 提高 mysql 在网络上的性能

c# - Winform继承和默认窗体大小

c# - 仅当焦点在文本框上时显示按钮