我正在尝试使用 Parallel.For
来加速一个冗长(几毫秒)的操作*,但是在该方法返回之前,我在我的 WinForms 应用程序中得到了 Paint 事件 - 建议它以某种方式触发消息泵。但是,整体重绘会导致在不一致的状态下访问数据,从而产生不稳定的错误和异常。我需要确保 Parallel.For
在阻塞时不会触发 UI 代码。
到目前为止,我对此的研究尚无定论,并粗略地指出了诸如同步上下文和 TaskScheduler
实现之类的东西,但我还没有弄清楚这一切。
如果有人可以帮助我解决一些问题,我将不胜感激。
- 导致
Parallel.For
触发 WinForms 消息泵的事件链是什么? - 有什么办法可以完全避免这种情况的发生?
- 或者,是否有任何方法可以判断 UI 事件处理程序是从常规消息泵调用,还是从
Parallel.For
触发的“繁忙”消息泵调用?
编辑: * 一些上下文:上面几毫秒的操作是游戏引擎循环的一部分,其中 16 毫秒可用于完整更新 - 因此属性“冗长”。这个问题的上下文是在它的编辑器中执行一个游戏引擎核心,它是一个 WinForms 应用程序。 Parallel.For
发生在内部引擎更新期间。
最佳答案
这来自 CLR,它实现了永远不允许 STA 线程(又名 UI 线程)阻塞在同步对象上的契约。就像 Parallel.For() 一样。它泵以确保不会发生死锁。
这会触发 Paint 事件,以及其他一些事件,确切的消息过滤是一个保密的 secret 。它与 DoEvents() 非常相似,但可能导致重入错误的东西被阻止了。比如用户输入。
但是很明显你有一个 DoEvents() 风格的错误,重入永远是一个讨厌的错误生成器。我怀疑您只需要设置一个 bool 标志以确保 Paint 事件跳过更新,这是最简单的解决方法。将 Program.cs 中 Main() 方法的 [STAThread] 属性更改为 [MTAThread] 也是一个简单的修复,但如果您还有正常的 UI,则风险很大。喜欢 private bool ReadyToPaint;
方法,推理起来最简单。
然而,您应该确切地调查为什么 Winforms 认为需要 Paint,它不应该,因为您可以控制游戏循环中的 Invalidate() 调用。它可能因用户交互而触发,例如最小/最大/恢复窗口,但这种情况应该很少见。地垫下隐藏着另一个 bug 的可能性非零。
关于c# - 为什么 Parallel.For 会执行 WinForms 消息泵,如何防止?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35535580/