Invoke() is blocking

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-04 16:58:16

问题


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:

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...

Thanks.

Thanks everybody.

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


回答1:


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




回答2:


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.

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

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

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

should be fine.




回答3:


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).

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.

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!!!).

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.

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.




回答4:


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?



来源:https://stackoverflow.com/questions/287142/invoke-is-blocking

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!