Console application does not obey Thread.Join

I am having trouble understanding why my console application is not waiting for the thread to complete, which it spawns completely . I think this is due to the fact that the thread in question also spawns its child threads and / or System.Timer inclusion

The main program stream is as follows. Main creates a new thread against the Simulator.Start method and then joins until this thread completes. Simulator.Start creates a new Timer (to limit its execution time), and then creates / starts a chain of child threads. When the Elapsed event rises to the Timer , this means that the Simulator must terminate all of its child threads and generate a report. The problem is that the console application terminates as soon as all the child threads terminate, and the code for creating the report never starts (see the Simulator.Stop Method below).

Hope some pseudo code helps:

 public class Program { private static Simulator _simulator; private static void Main(string[] args) { var options = new SimulationOptions(); //check for valid options if (!Parser.Default.ParseArguments(args, options)) return; _simulator = new Simulator(options); var thread = new Thread(_simulator.Start) {IsBackground = false}; thread.Start(); thread.Join(); } } public class Simulator { private readonly SimulationOptions _options; private readonly List<Thread> _threads = new List<Thread>(); private readonly List<Worker> _workers = new List<Worker>(); private static Timer _timer; public Simulator(SimulationOptions options) { _options = options; StartTimer(_options.LengthOfTest); } private void StartTimer(int lengthOfTest) { _timer = new Timer {Interval = lengthOfTest*1000}; _timer.Elapsed += Timer_Elapsed; _timer.Start(); } private void Timer_Elapsed(object sender, ElapsedEventArgs e) { _timer.Stop(); Stop(); } public void Stop() { // Request that the worker thread stop itself: foreach (Worker worker in _workers) { worker.RequestStop(); } GenerateReport(); //<-- this code never gets executed } private XDocument GenerateReport() { //build an awesome report } public void Start() { _threads.Clear(); _workers.Clear(); for (int i = 0; i < _options.NumberOfClients; i++) { _workers.Add(new Worker()); _threads.Add(new Thread(_workers.Last().PumpMessages)); _threads.Last().Start(); } } } public class Worker { private bool _shouldStop = false; public void PumpMessages() { while (!_shouldStop) { //does cool stuff until told to stop } } public void RequestStop() { _shouldStop = true; } } 
+4
source share
2 answers

Nothing in your start method supports a thread. When the next method ends, a thread also occurs. Then you call Thread.Join and this is the end of it.

 public void Start() { _threads.Clear(); _workers.Clear(); for (int i = 0; i < _options.NumberOfClients; i++) { _workers.Add(new Worker()); _threads.Add(new Thread(_workers.Last().PumpMessages)); _threads.Last().Start(); } } 

If you plan to wait until this work is completed, consider waiting in ManualResetEvent for each workflow used.

http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx http://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitall.aspx

Your method should look something like this:

 public void Start() { _threads.Clear(); _workers.Clear(); var evts = new List<ManualResetEvent>() for (int i = 0; i < _options.NumberOfClients; i++) { ManualResetEvent evt = new ManualResetEvent(false); evts.Add(evt); _workers.Add(new Worker(evt)); _threads.Add(new Thread(_workers.Last().PumpMessages)); _threads.Last().Start(); } WaitHandle.WaitAll(evts.ToArray()); } public class Worker { private bool _shouldStop = false; private readonly ManualResetEvent @event; public Worker(ManualResetEvent @event) { this.@event = @event; } public void PumpMessages() { while (!_shouldStop) { //does cool stuff until told to stop } @event.Set(); } public void RequestStop() { _shouldStop = true; } } 
+3
source

The Join method only waits for the instance of the thread that you joined, so Simulator.Start just creates some threads, and it ends because the Join result is returned and your main thread ends. But still, your application is live (the reason some other Foreground threads continue to work).

 generate a report never gets executed? Why? 

The process will end when all Foreground Threads complete. therefore, as soon as your child threads return from the PumpMessages method, when you call RequestStop in a loop, all your foreground threads terminate

 public void Stop() { // Request that the worker thread stop itself: foreach (Worker worker in _workers) { worker.RequestStop(); } <--here all foreground threads are ready to terminate GenerateReport(); //<-- this code never gets executed } 

There was little misleading that I said that all foreground threads die after the loop . To make it clear, let's say that we gave instructions so that worker threads stop working, so all threads may or may not die before executing the GenerateReport method. yes there is Race If work flows win the race, we lose it and vice versa. sometimes your GenerateReport can run without any problems.

How to fix it? We are just waiting for the completion of all our work flows. what he.

 public void Start() { _threads.Clear(); _workers.Clear(); for (int i = 0; i < _options.NumberOfClients; i++) { _workers.Add(new Worker()); _threads.Add(new Thread(_workers.Last().PumpMessages)); _threads.Last().Start(); } foreach (var t in _threads) t.Join(); } 
+2
source

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


All Articles