Closing side thread before closing the main mold

I am creating a GUI application with window forms in Visual Studio .NET 2010 using C# . I have several threads, for example:

  • The stream that pings the target device to ensure that the connection is not lost
  • a stream that requests and receives measurements from the device
  • A topic that constantly updates the main form with these results. This thread causes changes to the main elements of the form using Control.Invoke() . My goal: I want to make sure the form is closed properly when the user clicks (x) on the main form. This means that I want my connection to close, all side streams to stop, etc. I am trying to use the FormClosing event. There I set the KillThreads flag to true and waited for the side threads to complete. Every check has something like
 if(KillThreads) return; 

But there's a problem. If I try to wait for all threads to complete

 for(;;) if(ActiveSideThreads == 0) break; closeConnection(); 

thread N3 freezes, blocked by Invoke() ; Of course, this is because the main gui thread is in an infinite loop. Thus, the closing of the form is delayed forever.

If I implement the operations before closing as a procedure of thread 4 and create a new thread in the FromClosing event handler, the form closes immediately, before the completion of thread 4 and Invoke() throws an error - t. 4 could not close thread 3 in time. If I add Join(), Invoke() , the calls will be blocked again, and thread 3 will never end, so thread 4 will wait forever.

What should I do? Is there any way to solve this problem?

+4
source share
4 answers

First of all, decide if you really need to use Invoke at all. I think this is a long time ago, my opinion is that Invoke is one of the most abusive methods for invoking an action in the user interface thread after it has been initiated from the workflow. An alternative is to create some type of message object in the workflow that describes the action that needs to be performed on the user interface thread and put it in a common queue. Then, the user interface thread will examine this queue at some interval using System.Windows.Forms.Timer and invoke each action. This has several advantages.

  • It breaks the tight connection between the user interface and the workflows that Invoke imposes.
  • He is responsible for updating the user interface thread in the user interface thread, where he must belong in any case.
  • The user interface flow determines when and how often an update should occur.
  • There is no risk of user interface message overflow, as would be the case with marshaling methods initiated by a workflow.
  • The workflow should not wait for confirmation that the update has been completed before proceeding with the next steps (i.e. you will get more bandwidth for both the user interface and workflows).
  • It is much easier to handle shutdown operations, as you can pretty much eliminate all the race conditions that are usually present when a workflow request is legitimately completed.

Of course, Invoke very useful, and there are many reasons to continue using this approach. If you decide to continue using Invoke , then read more.

One idea is to set the KillThreads flags in the KillThreads event, and then cancel closing the form by setting FormClosingEventArgs.Cancel = true . You can inform the user of the completion of the completion of work. You can disable some controls on the form so that new actions cannot be initiated. Thus, we basically signal that a shutdown was requested, but they postpone closing the form until the workflows stop first. To do this, you can run a timer that periodically checks to see if the worker threads have completed, and if they are, you can call Form.Close .

 public class YourForm : Form { private Thread WorkerThread; private volatile bool KillThreads = false; private void YourForm_Closing(object sender, FormClosingEventArgs args) { // Do a fast check to see if the worker thread is still running. if (!WorkerThread.Join(0)) { args.Cancel = true; // Cancel the shutdown of the form. KillThreads = true; // Signal worker thread that it should gracefully shutdown. var timer = new System.Timers.Timer(); timer.AutoReset = false; timer.SynchronizingObject = this; timer.Interval = 1000; timer.Elapsed = (sender, args) => { // Do a fast check to see if the worker thread is still running. if (WorkerThread.Join(0)) { // Reissue the form closing event. Close(); } else { // Keep restarting the timer until the worker thread ends. timer.Start(); } }; timer.Start(); } } } 

The code is above Join calls, but it defines a timeout of 0, which causes the Join call to return immediately. This should contain user interface redirection messages.

+6
source

I would have a top-level exception handler in your thread executing Invoke that swallows the exceptions thrown from Invoke if it detects KillThreads , this is true. Then all you need is for the main thread to set KillThreads to true and allow the end of the message loop.

My common technique, rather than a simple boolean variable, would be to use ManualResetEvent as a signal to terminate other threads. The main reason for this is because I have long streams other than UI that sometimes want to sleep. Using the event, I can replace calls on Thread.Sleep with calls to Event.WaitOne , which means that the thread will always wake up immediately if it should interrupt.


In any case, I usually do not wait for threads to complete. I believe them to do this in a timely manner and do not tend to end with subsequent code that relies on threads that do not work. If you have such a circumstance, perhaps you could clarify what it is?

+1
source

You can call Thread.Join for all threads and kill with Thread.Abort if they cannot be merged within the specified timeout. An Abort call throws a ThreadAbortException in the context of the thread that terminates this thread.

0
source

I think the main point (whether the main thread should wait for other threads to finish or not) is to check whether KillThreads is false before invoking operations in the GUI thread, if you really do not want to show the user something before exiting, in this case, you should just loop in the main thread, check the ThreadState or IsAlive properties of all the worker threads, determine if they are already finished, and then exit (or do whatever is necessary after all the worker threads are executed).

0
source

All Articles