C# Invoke() 被阻塞

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/287142/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 21:44:10  来源:igfitidea点击:

Invoke() is blocking

c#.netwinforms.net-2.0

提问by EricSchaefer

From time to time my applications GUI stops redrawing. There a lot of threads that are firing all kinds of events (like timers or network data ready etc.). Also there are a lot of controls that are subscribing these events. Because of that, all the event handlers play the InvokeRequired/Invoke game. Now I figured out that when the GUI freezes a lot of threads are waiting for Invoke() to return. Looks like the message pump stopped pumping. The handlers look like this:

我的应用程序 GUI 有时会停止重绘。有很多线程正在触发各种事件(例如计时器或网络数据就绪等)。还有很多控件订阅这些事件。因此,所有事件处理程序都玩 InvokeRequired/Invoke 游戏。现在我发现当 GUI 冻结时,很多线程都在等待 Invoke() 返回。看起来消息泵停止泵送。处理程序如下所示:

private void MyEventHandler( object sender, EventArgs e ) {
    if ( InvokeRequired ) {
        Invoke( new EventHandler( MyEventHandler ), sender, e );
        return;
    }

    SetSomeStateVariable();
    Invalidate();
}

Any ideas?

有任何想法吗?

Solution: BeginInvoke(). Looks like you should always use BeginInvoke() if you have lots of CrossThread-Events...

解决方案:BeginInvoke()。如果你有很多交叉线程事件,看起来你应该总是使用 BeginInvoke() ......

Thanks.

谢谢。

Thanks everybody.

谢谢大家。

EDIT:Looks like BeginInvoke()really solved it. No freezing until now.

编辑:看起来BeginInvoke()真的解决了。直到现在都没有冻结。

采纳答案by Lou Franco

Invoke waits until the event is handled in the GUI thread. If you want it to be asynchronous use BeginInvoke()

Invoke 等待事件在 GUI 线程中处理。如果您希望它是异步的,请使用 BeginInvoke()

回答by Jon Skeet

Deadlock perhaps? Do you make sure that the events are never fired while holding a lock?

可能是死锁?您是否确保在持有锁时永远不会触发事件?

Are you able to see this with a debugger attached? If so, make it freeze and then hit the "pause" button - and see what the UI thread is doing.

您是否能够在附加调试器的情况下看到这一点?如果是这样,让它冻结,然后点击“暂停”按钮 - 看看 UI 线程在做什么。

Note that if you are able to get away with BeginInvoke instead of Invoke, life is a bit easier as it won't block.

请注意,如果您能够使用 BeginInvoke 而不是 Invoke,那么生活会轻松一些,因为它不会阻塞。

Also note that you don't need the "new EventHandler" bit - just

另请注意,您不需要“new EventHandler”位 - 只需

Invoke((EventHandler) MyEventHandler, sender, e);

should be fine.

应该没事。

回答by Marc Gravell

The most likely answer (deadlock) has already been suggested.

最可能的答案(死锁)已经被提出。

Another way to simulate this behaviour is to reduce the number of pool threads and IO completion ports; you haven't called ThreadPool.SetMaxThreads(...)by any chance?

另一种模拟这种行为的方法是减少池线程和IO完成端口的数量;你没有打过电话ThreadPool.SetMaxThreads(...)吗?

回答by Marc Gravell

From watching this question, I can see that you're not going to get any answers that will fix the problem immediately, as most of them require you to debug the event, and it happens so infrequently that this is nearly impossible. So, let me suggest you make some code changes that might help you identify the culprit in the field.

通过观看这个问题,我可以看到您不会得到任何可以立即解决问题的答案,因为大多数答案都需要您调试事件,而且这种情况很少发生,这几乎是不可能的。因此,让我建议您进行一些代码更改,以帮助您识别该领域的罪魁祸首。

I suggest that you create a static class whose sole purpose is to handle all your Invoke calls. I would suggest that this class has a method that takes a Control, (to call Invoke on) an Action (the method to be invoked), and a description (containing the information you would need to know to identify the method and what it is going to do).

我建议您创建一个静态类,其唯一目的是处理所有 Invoke 调用。我建议这个类有一个方法,它接受一个 Control,(调用 Invoke)一个 Action(要调用的方法)和一个描述(包含你需要知道的信息来识别方法和它是什么将要做)。

Within the body of this method, I suggest you enqueue this information (method, description) and return immediately.

在此方法的主体内,我建议您将这些信息(方法、描述)加入队列并立即返回。

The queue should be serviced by a single thread, which pops the action/message pair off the queue, records the current time and the Action's description in a pair of properties, and then Invokes() the Action. When the Action returns, the description and time are cleared (your DateTime can be nullable, or set it to DateTime.Max). Note, since all Invokes are marshalled one at a time onto the UI thread, you're not losing anything by servicing the queue by a single thread here.

该队列应由单个线程提供服务,该线程将动作/消息对从队列中弹出,在一对属性中记录当前时间和动作的描述,然后调用()该动作。当 Action 返回时,说明和时间被清除(您的 DateTime 可以为空,或将其设置为 DateTime.Max)。请注意,由于所有 Invokes 一次一个编组到 UI 线程上,因此此处由单个线程为队列提供服务不会丢失任何内容。

Now, here's where we get to the point of this. Our Invoking class should have a heartbeat System.Threading.Timer thread. This should NOT be a windows.forms.timer object, as that runs on the UI thread (and would be blocked when the ui is blocked!!!).

现在,这就是我们的重点。我们的 Invoking 类应该有一个心跳 System.Threading.Timer 线程。这不应该是 windows.forms.timer 对象,因为它在 UI 线程上运行(并且会在 ui 被阻塞时被阻塞!!!)。

The job of this timer is to periodically peek at the time the current Action was Invoked. If DateTime.Now - BeginTime > X, the heartbeat timer will decide that this Action has blocked. The heartbeat timer will LOG (however you log) the DESCRIPTION recorded for that Action. You now have a recording of what was happening at the time your UI locked up and can debug it better.

这个计时器的工作是定期查看当前操作被调用的时间。如果 DateTime.Now - BeginTime > X,心跳计时器将判定此 Action 已被阻止。心跳计时器将记录(无论您如何记录)为该操作记录的描述。您现在可以记录您的 UI 锁定时发生的情况,并且可以更好地对其进行调试。

I know it's not an answer to your problem, but at least by doing this you can get a good idea about what's going on at the time you're blocked.

我知道这不是您问题的答案,但至少通过这样做,您可以很好地了解您被阻止时发生的情况。