我有几个异步方法需要同步回主 ui 线程。
async Task MyAsyncMethod() {
await DoSomeThingAsync().ConfigureAwait(true);
DoSomethingInTheGui();
}
现在我需要从一个由 gui 线程触发的同步事件处理程序调用它们,并且在异步方法完成之前事件处理程序无法完成。所以 MyAsyncMethod().Wait() 不是一个选项,也不是某种一劳永逸的解决方案。
这种使用 Nito.AsyncEx 的方法看起来很有前途,但它仍然死锁:https://stackoverflow.com/a/9343733/249456
我找到的唯一解决方案对我来说似乎是一个 hack:
public void RunInGui(Func<Task> action)
{
var window = new Window();
window.Loaded += (sender, args) => action()
.ContinueWith(p => {
window.Dispatcher.Invoke(() =>
{
window.Close();
});
});
window.ShowDialog();
}
有没有办法在不创建新窗口的情况下获得相同的效果(阻止调用方法,允许同步回 gui 线程)?
注意:我知道重构可能是最好的选择,但这是一项艰巨的任务,我们必须在更长的时间内完成。另外值得一提的是,这是 Autodesk Inventor 的插件。该 api 有几个怪癖,所有 API 调用(甚至与 ui 无关的调用)都必须从主/ui 线程执行。
另外值得一提的是,我们保留了对主线程调度程序的引用,并在整个代码库中使用了 MainThreadDispatcher.InvokeAsync(() => ... )。
请您参考如下方法:
The only solution i've found seems like a hack to me
所有同步异步解决方案都是 hack。我在 article on brownfield async 中列举了最常见的那些.请务必注意,不存在针对任意代码的解决方案。 每个解决方案在某些情况下要么死锁要么抛出异常。
一种更简单的技巧是阻塞线程池线程:
Task.Run(() => action()).Wait();
但是,如果您的 action() 需要 UI 上下文,那么这将不起作用。
下一步是尝试使用单线程上下文。这是我的 AsyncContext 类型采用的方法:
AsyncContext.Run(() => action());
如果 action 正在等待 UI 线程的消息队列被抽取,这将死锁。由于此死锁但 Window 有效,因此情况似乎如此。
进一步下降一个级别,您可以使用 nested pumping .请注意 nested pumping opens up a whole world of hurt due to unexpected reentrancy . GitHub 上有一些代码显示 how to do nested pumping on WPF .但是,我强烈建议您重构代码而不是拥抱可重入。
