为什么Parallel.For执行WinForms消息泵,以及如何防止它? [英] Why does Parallel.For execute the WinForms message pump, and how to prevent it?

查看:74
本文介绍了为什么Parallel.For执行WinForms消息泵,以及如何防止它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Parallel.For 来加快冗长的操作(几毫秒)*,但是我之前在WinForms应用程序中都得到了Paint事件该方法已返回-建议以某种方式触发消息泵。但是,整体重绘会导致以不一致的状态访问数据,从而产生错误的错误和异常。我需要确保 Parallel.For 在阻止时不会触发UI代码。

I'm trying to speed up a lengthy (a few ms) operation* using Parallel.For, but I'm getting Paint events all over my WinForms application before the method has returned - suggesting it somehow triggers a message pump. The overall redraw, however, leads to accessing data in an inconsistent state, producing erratic errors and exceptions. I need to assure that Parallel.For, while blocking, doesn't trigger UI code.

我对到目前为止,还没有定论,并大致向我指出了诸如同步上下文和 TaskScheduler 实现之类的东西,但是我还没有弄清这一切。

My research on this so far has been inconclusive and pointed me roughly to things like synchronization contexts and TaskScheduler implementations, but I have yet to make sense of it all.

如果有人可以通过清理一些东西来帮助我,那将不胜感激。

If someone could help me along the way by clearing some things up, that would very much be appreciated.


  1. 导致 Parallel.For 触发WinForms消息泵的事件链是什么?

  2. 有什么办法可以防止这种情况发生?

  3. 或者,是否有任何方法可以判断是从常规消息泵中调用UI事件处理程序,还是由<$ c $触发的忙消息泵被调用? c> Parallel.For ?

  1. What is the chain of events that leads to Parallel.For triggering WinForms message pump?
  2. Is there any way I can prevent this from happening entirely?
  3. Alternatively, is there any way to tell if a UI event handler is called from the regular message pump, or the "busy" message pump as triggered by Parallel.For?






编辑: *一些上下文:以上几毫秒的操作是游戏引擎循环的一部分,其中16毫秒可用于完整更新e-因此属性为长。此问题的上下文是在其编辑器(即WinForms应用程序)中执行游戏引擎核心。 Parallel.For 在内部引擎更新期间发生。


* Some context: The above few ms operation is part of a game engine loop, where 16 ms are available for a full update - hence the attribute "lengthy". The context of this problem is executing a game engine core inside its editor, which is a WinForms application. Parallel.For happens during the internal engine update.

推荐答案

从CLR开始,它实现了这样的约定,即永远不允许STA线程(即UI线程)在同步对象上进行阻塞。就像Parallel.For()一样。

This comes from the CLR, it implements the contract that an STA thread (aka UI thread) is never allowed to block on a synchronization object. Like Parallel.For() does. It pumps to ensure that no deadlock can occur.

这会触发Paint事件,而在其他一些事件中,确切的消息过滤是一个很好的秘密。它与DoEvents()非常相似,但是可能会导致重新进入错误的内容已被阻止。就像用户输入一样。

This gets Paint events to fire, and some others, the exact message filtering is a well-kept secret. It is pretty similar to DoEvents() but the stuff that is likely to cause re-entrancy bugs blocked. Like user input.

但是很明显,您在黑桃中有一个DoEvents()样式的错误,重新进入永远是一个令人讨厌的错误生成器。我怀疑您只需要设置一个 bool 标志即可确保Paint事件跳过更新,最简单的解决方法。将Program.cs中Main()方法上的[STAThread]属性更改为[MTAThread]也是一个简单的解决方法,但是如果您也具有正常的UI,则存在很大的风险。赞成 private bool ReadyToPaint; 的方法,这是最简单的推理方法。

But clearly you have a DoEvents() style bug in spades, re-entrancy is forever a nasty bug generator. I suspect you'll need to just set a bool flag to ensure that the Paint event skips an update, simplest workaround. Changing the [STAThread] attribute on the Main() method in Program.cs to [MTAThread] is also a simple fix, but is quite risky if you also have normal UI. Favor the private bool ReadyToPaint; approach, it is simplest to reason through.

但是您应该确切地调查为什么Winforms认为需要Paint,而不必这样做,因为您可以控制游戏循环中的Invalidate()调用。由于用户交互(例如最小/最大/还原窗口),它可能会触发,但这种情况很少见。非零的可能性是在地板垫下还隐藏着另一个虫子。

You should however investigate exactly why Winforms thinks that Paint is needed, it shouldn't since you are in control over the Invalidate() call in a game loop. It may fire because of user interactions, like min/max/restoring the window but that should be rare. Non-zero odds that there's another bug hidden under the floor mat.

这篇关于为什么Parallel.For执行WinForms消息泵,以及如何防止它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆