Notify when a thread completes without blocking the calling thread

I am working on an outdated application that is built on top of NET 3.5. This is a limitation that I cannot change. I need to execute a second thread in order to complete a long-term task without blocking the user interface. When the thread is complete, I need to call back.

Now I tried this pseudo code:

Thread _thread = new Thread(myLongRunningTask) { IsBackground = True }; _tread.Start(); // wait until it done _thread.Join(); // execute finalizer 

The second option, which does not block the user interface, is as follows:

 Thread _thread = new Thread(myLongRunningTask) { IsBackground = True }; _tread.Start(); // wait until it done while(_thread.IsAlive) { Application.DoEvents(); Thread.Sleep(100); } // execute finalizer 

Of course, the second solution is not a good reason to recharge the user interface. What is the correct way to make a callback when _thread completes? Also, how do I know if a thread has been canceled or interrupted?

* Note: * I cannot use BackgroundWorker, and I cannot use the Async library, I need to work with the native thread class.

+12
multithreading c #
source share
7 answers

The easiest way is to add a callback, basically. You can even do this simply by using the way multicast delegates work:

 ThreadStart starter = myLongRunningTask; starter += () => { // Do what you want in the callback }; Thread thread = new Thread(starter) { IsBackground = true }; thread.Start(); 

This is very vanilla, and the callback will not start if the thread is interrupted or throws an exception. You can wrap it in a class using several callbacks or a callback that determines the status (interrupted, throws an exception, etc.) and processes it by wrapping the original delegate, calling it in the method with try / catch block and execute the callback call accordingly.

If you do not take any special actions, the callback will be executed in the background thread, so you will need to use Control.BeginInvoke (or something else) to return to the user interface thread.

+36
source share

I absolutely understand your requirements, but you missed one important thing: do you really need to wait for this thread to finish synchronously? Or maybe you just need to execute the “finalizer” after detecting the end of the stream?

In the latter case, just wrap the call to myLongRunningTask in another method:

 void surrogateThreadRoutine() { // try{ .. mytask(); // finally { .. ..all 'finalization'.. or ie raising some Event that you'll handle elsewhere } 

and use it as a thread routine. Thus, you will find out that finalization will occur in the stream and immediately after the end of the actual task.

However, of course, if you work with some user interfaces or other schedulers, now the “finalization” will work on your thread , and not on the “normal threads” of your interface or comm., You will need to make sure that all resources are external in relation to your streaming task, correctly protected or synchronized, otherwise you are likely to come across other application streams.

For example, in WinForms, before you touch any user interface elements from the finalizer, you will need Control.InvokeRequired (surely = true) and Control.BeginInvoke / Invoke to return the context back to the user interface stream.

For example, in WPF, before touching any user interface elements from the finalizer, you will need Dispatcher.BeginInvoke ..

Or, if a collision can occur with any threads you control, a simple lock() enough. and etc.

+2
source share

You can use a combination of custom event and use BeginInvoke :

 public event EventHandler MyLongRunningTaskEvent; private void StartMyLongRunningTask() { MyLongRunningTaskEvent += myLongRunningTaskIsDone; Thread _thread = new Thread(myLongRunningTask) { IsBackground = true }; _thread.Start(); label.Text = "Running..."; } private void myLongRunningTaskIsDone(object sender, EventArgs arg) { label.Text = "Done!"; } private void myLongRunningTask() { try { // Do my long task... } finally { this.BeginInvoke(Foo, this, EventArgs.Empty); } } 

I checked it works in .NET 3.5

+1
source share

Try using ManualRestEvent to signal the end of the stream.

0
source share

You can use the observer pattern, look here:

http://www.dofactory.com/Patterns/PatternObserver.aspx

The observer pattern allows you to notify other objects that were previously identified as an observer.

0
source share

It is possible to use conditional variables and a mutex, or some functions, such as wait (), signal (), perhaps timed wait (), so as not to block the main thread indefinitely.

In C #, it will be:

  void Notify() { lock (syncPrimitive) { Monitor.Pulse(syncPrimitive); } } void RunLoop() { for (;;) { // do work here... lock (syncPrimitive) { Monitor.Wait(syncPrimitive); } } } 

more about this here: C # / state variables. NET

This is the concept of a Monitor object in C #, you also have a version that allows you to set a timeout

 public static bool Wait( object obj, TimeSpan timeout ) 

more about this here: https://msdn.microsoft.com/en-us/library/system.threading.monitor_methods(v=vs.110).aspx

0
source share
  1. Very simple thread of execution with completion callback
  2. This does not have to work in monaural mode and is just used for convenience.
 using System; using System.Collections.Generic; using System.Threading; using UnityEngine; public class ThreadTest : MonoBehaviour { private List<int> numbers = null; private void Start() { Debug.Log("1. Call thread task"); StartMyLongRunningTask(); Debug.Log("2. Do something else"); } private void StartMyLongRunningTask() { numbers = new List<int>(); ThreadStart starter = myLongRunningTask; starter += () => { myLongRunningTaskDone(); }; Thread _thread = new Thread(starter) { IsBackground = true }; _thread.Start(); } private void myLongRunningTaskDone() { Debug.Log("3. Task callback result"); foreach (int num in numbers) Debug.Log(num); } private void myLongRunningTask() { for (int i = 0; i < 10; i++) { numbers.Add(i); Thread.Sleep(1000); } } } 
0
source share

All Articles