Issues related to displaying MessageBox from non-GUI threads

I am working on Win.Forms data-bound applications, where I discovered strange behavior. The application has separate I / O streams that receive updates via asynchronous web requests, which are then sent to the main / graphical interface stream for processing and updating application data stores (which, in turn, can be bound to various GUI elements, etc. .). The server at the other end of the web request requires periodic requests or session time.

I reviewed several attempts to solve thread problems, etc., and I noticed the following behavior:

  • If I use Control.Invoke to send updates from I / O streams to the main stream, and this update causes the MessageBox to appear, the main form message pump stops until the user clicks the ok button. It also blocks the continuation of the I / O stream, which ultimately leads to timeouts on the server.

  • If I use Control.BeginInvoke to send updates from input / output streams to the main stream, the main message flow of the message does not stop, but if the processing of the update leads to the display of the message window, the processing of the rest of this update is suspended until the user types OK . As I / O streams continue to work and message processing is retained on the message pump, some BeginInvoke calls may be called before the message with the message field ends. This results in invalid out-of-line updates.

  • I / O-threads add updates to the lock queue (very similar to Creating Queue <T> Lock in .NET? ). The GUI-thread uses Forms.Timer, which periodically applies all updates to the lock queue. This solution solves both the problem of blocking I / O streams and the sequence of updates, that is, the next update will never be launched until the previous one is completed. Nevertheless, there is a small cost of execution, as well as the appearance of a delay in showing updates that are unacceptable in the long term. I would like for updates to be processed in the main thread using events rather than polling.

So to my question. How can I do it:

  • avoid blocking input / output streams
  • ensure that updates are completed in sequence
  • keep the main message pump on, showing the message box as a result of the update.

Update: see solution below

+4
source share
4 answers

Here is the solution I ended up with:

  • The I / O thread puts all updates in a thread-safe / blocking queue.
  • Separate threads of the workflow infinitely disinfect updates, and then BeginInvoke them into the GUI thread.
  • Displaying a MessageBox in a GUI thread in response to updates is now done using BeginInvoke.

This solution has the following advantages compared to the previous one (described in paragraph 3. above using a poll for GUI updates):

  • GUI event update, not polling. This gives both (theoretically) better performance and less latency.
  • Neither in the GUI updates, nor in / in the message box is blocked.

Update: It seems that GUI updates are still blocked while a message is displayed using this solution. Will be updated if this is fixed.

Update 2: Updated with a fix for the workflow by changing Invoke to BeginInvoke.

0
source

MessageBox itself configures the message loop. Of course, this will not be a Windows Forms message loop. Everything works as usual, but minus sending requests to call delegates sent by Control.BeginInvoke (). Only a Windows Forms message loop can do this.

This occurs when a MessageBox.Show () call is made in the user interface thread. But not when this is done in a workflow, message queues are a per-thread property. If you can pass on a Show call that will be delegated to the employee, you are likely to solve your problem.

The solution to your questions:

  • You really need the opposite: worker threads must block. Not blocking can cause serious problems, the BeginInvoke send queue will be filled without restrictions. One possible trick is counting the number of BeginInvoke calls, counting down in the delegate’s goal. Use the Interlocked class.

  • The order of fulfillment of BeginInvoke goals is guaranteed. The real problem is probably due to the fact that worker threads are not synchronized.

  • Show the message box in the stream.

+5
source

So, you have a complicated data collection and processing chain that you want to continue, but then you insert a MessageBox there. Nothing in Threading + Invoke will change the fact that the MessageBox is modal, and you need to wait for it to close, making the whole chain depend on the user to click something.

So, get rid of MessageBox, at least in the main way. If a processing segment requires user intervention, then this segment must be in a separate thread.

+1
source

Do not use Forms.Timer to apply updates from the queue, but use a different thread to do this. This thread constantly monitors the queue and (possibly) tells the GUI when it is updated with new data (via BeginInvoke). A MessageBox can be displayed from this read stream in a queue - it is not necessary to be a GUI stream.


Edit: Queue consumer can call Control.Invoke to display a Box message to work around the z-order problem

+1
source

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


All Articles