WinForms message context does not respond

I intentionally abuse the message outline in a Windows Forms application, but my “just for fun” project quickly went beyond my comprehension. While the task is running, the form does not respond. Yes, there are many other similar questions, but in my case, I deliberately avoid working on another thread (to win a bet on myself?)

I have a function that runs (many) short snippets of time in a UI thread: get_IsComplete() checks if the job is complete; DoWork() from 0 to 1000 (only to maintain the central processor). The task is started by calling control.BeginInvoke(new Action(ContinueWith), control); and then it (tail recursively) calls itself to completion, always performing a short piece of work with the user interface thread.

 public void ContinueWith(Control control) { if (!IsComplete) { DoWork(); OnNext(control); control.BeginInvoke(new Action(ContinueWith), control); } else { OnCompleted(control); } } 

I expected the application to handle other events (mouse clicks, control replicas, form movement, etc.), but it seems that my calls are becoming more priority than I would like.

Any suggestions?

+6
source share
3 answers

The control.BeginInvoke () call puts the delegate that you are passing to the internal queue and calls PostMessage () to wake up the message outline and pay attention. This is what the first BeginInvoke gets. Any input events (mouse and keyboard) are also sent to the message queue, Windows places them there.

The behavior you did not expect is in the code that runs when you receive a sent message. It does not just cancel the call request and executes it, it loops until the entire call queue is empty. How your code works, this queue is never cleared, because a call to ContinueWith () adds another call request. Thus, it simply continues to loop and process call requests and never manages to get more messages from the message queue. Or else: swapping call queues rather than message queues.

Incoming messages remain in the message queue until your code stops adding more call requests and regular loop cycle overflows resume after your code stops recursing. Your interface will look frozen until this happens, because Paint events will not be delivered either. They are generated only when the message queue is empty.

It is important that it works the way it does, calling PostMessage () is not guaranteed to work. Windows does not allow more than 10,000 messages in the message queue. But Control.BeginInvoke () has no such limit. By emptying the call queue, a lost PostMessage message does not cause any problems. However, this behavior causes other problems. The classic method often calls BackgroundWorker.ReportProgress (). The same behavior, the UI thread is simply flooded with call requests and no longer bypasses its usual duties. Frowning upside down, who comes across this: "I'm using BackgroundWorker, but my user interface is still freezing."

Anyhoo, your experiment is a terrible failure. A call to Application.DoEvents () would require the message queue to be empty. About many measures with this, check this answer for details. The upcoming async keyword support will provide another way to do this. Not sure if he treats message priority differently. I pretty much doubt it, Control.BeginInvoke () is pretty core. One problem is to use a timer with a very short interval. Timer messages also enter the message queue (sort of), but they have a very low priority. Input events are processed first. Or hack low: PostMessage is called with your own message and overrides WndProc to detect it. It is a little hidden and narrow. The Application.Idle event is useful for processing after receiving any input events.

+16
source

Try adding a challenge.

 Application.DoEvents() 

somwhere in your ContinueWith () method.

And please, do not use such a "thread" style for anything other than experimentation .;).

Source: https://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents(v=vs.110).aspx

0
source

Use begininvoke overload, which takes precedence. "Normal" priority is higher than input and rendering. You need to choose something like applicationidle

0
source

Source: https://habr.com/ru/post/893350/


All Articles