我们有一个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相关的消息? 最佳答案
无需利用现有的消息泵,而是可以在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/