Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Waiting for thread to exit?

Waiting for thread to exit?

Scheduled Pinned Locked Moved C#
data-structuresdebuggingquestion
28 Posts 5 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    SledgeHammer01
    wrote on last edited by
    #1

    I have a scenario where the application starts up and spawns a worker thread. When the main thread exits, I want the PROCESS (and the worker thread) to stay alive until its done doing its thing and then the whole process can exit. So I did something like this:

    Thread threadCurrent = Thread.CurrentThread;

    		Task.Run(() =>
    		{
    			while (threadCurrent.IsAlive || (\_queue.Count > 0))
    				Thread.Sleep(250);
    		});
    

    This does exactly what I want. When the main application exits, the process keeps running until the queue is finished processing (_queue is processed in a separate thread, this code piece keeps the process alive) and then the process shuts down. I'm not happy with the Sleep(250) portion because that part is going for the entire lifetime of the application. It's not spinning the CPU or anything. I'm just looking for a better way to do it. I tried threadCurrent.Join(), however, that only works if the app is running in the debugger. I don't get why. If I have a debugger attached, the Join() blocks until the main thread is done as expected. If I don't have a debugger attached, the Join() never returns. EDIT: I should also note that this code in a library, so I can't really require the developer to do anything special in the main thread to make this work.

    P G 2 Replies Last reply
    0
    • S SledgeHammer01

      I have a scenario where the application starts up and spawns a worker thread. When the main thread exits, I want the PROCESS (and the worker thread) to stay alive until its done doing its thing and then the whole process can exit. So I did something like this:

      Thread threadCurrent = Thread.CurrentThread;

      		Task.Run(() =>
      		{
      			while (threadCurrent.IsAlive || (\_queue.Count > 0))
      				Thread.Sleep(250);
      		});
      

      This does exactly what I want. When the main application exits, the process keeps running until the queue is finished processing (_queue is processed in a separate thread, this code piece keeps the process alive) and then the process shuts down. I'm not happy with the Sleep(250) portion because that part is going for the entire lifetime of the application. It's not spinning the CPU or anything. I'm just looking for a better way to do it. I tried threadCurrent.Join(), however, that only works if the app is running in the debugger. I don't get why. If I have a debugger attached, the Join() blocks until the main thread is done as expected. If I don't have a debugger attached, the Join() never returns. EDIT: I should also note that this code in a library, so I can't really require the developer to do anything special in the main thread to make this work.

      P Offline
      P Offline
      PIEBALDconsult
      wrote on last edited by
      #2

      Will Thread.Join work better? http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx[^]

      S 1 Reply Last reply
      0
      • P PIEBALDconsult

        Will Thread.Join work better? http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx[^]

        S Offline
        S Offline
        SledgeHammer01
        wrote on last edited by
        #3

        As I specifically mentioned in my post, Thread.Join() only works on the main thread if the debugger is attached. I guess the debugger kills the main thread while without the debugger, the thread is not killed.

        P 1 Reply Last reply
        0
        • S SledgeHammer01

          I have a scenario where the application starts up and spawns a worker thread. When the main thread exits, I want the PROCESS (and the worker thread) to stay alive until its done doing its thing and then the whole process can exit. So I did something like this:

          Thread threadCurrent = Thread.CurrentThread;

          		Task.Run(() =>
          		{
          			while (threadCurrent.IsAlive || (\_queue.Count > 0))
          				Thread.Sleep(250);
          		});
          

          This does exactly what I want. When the main application exits, the process keeps running until the queue is finished processing (_queue is processed in a separate thread, this code piece keeps the process alive) and then the process shuts down. I'm not happy with the Sleep(250) portion because that part is going for the entire lifetime of the application. It's not spinning the CPU or anything. I'm just looking for a better way to do it. I tried threadCurrent.Join(), however, that only works if the app is running in the debugger. I don't get why. If I have a debugger attached, the Join() blocks until the main thread is done as expected. If I don't have a debugger attached, the Join() never returns. EDIT: I should also note that this code in a library, so I can't really require the developer to do anything special in the main thread to make this work.

          G Offline
          G Offline
          Garth J Lancaster
          wrote on last edited by
          #4

          I'd have been tempted to use

          WaitHandle.WaitOne();

          possibly with the timeout as a parameter in the WaitOne method - but then again, I likely havnt dabbled as much in threading as you, so I'll shut my pie-hole and watch for a better answer http://msdn.microsoft.com/en-us/library/System.Threading.WaitHandle.WaitOne(v=vs.110).aspx[^]

          1 Reply Last reply
          0
          • S SledgeHammer01

            As I specifically mentioned in my post, Thread.Join() only works on the main thread if the debugger is attached. I guess the debugger kills the main thread while without the debugger, the thread is not killed.

            P Offline
            P Offline
            PIEBALDconsult
            wrote on last edited by
            #5

            SledgeHammer01 wrote:

            only works on the main thread if the debugger is attached

            That is definitely untrue. I use Join all the time.

            S 1 Reply Last reply
            0
            • P PIEBALDconsult

              SledgeHammer01 wrote:

              only works on the main thread if the debugger is attached

              That is definitely untrue. I use Join all the time.

              S Offline
              S Offline
              SledgeHammer01
              wrote on last edited by
              #6

              Not what I'm seeing. If I do threadCurrent.Join() it works as expected IF THE DEBUGGER IS ATTACHED. When the main thread "is done" and my queue is empty, the whole process exits. If I ctrl-F5 it, it does keep the process alive, but it never gets past the threadCurrent.Join() and the process stays alive forever. Keep in mind that my worker thread is a FOREGROUND thread. That is required to keep the process alive.

              D 1 Reply Last reply
              0
              • S SledgeHammer01

                Not what I'm seeing. If I do threadCurrent.Join() it works as expected IF THE DEBUGGER IS ATTACHED. When the main thread "is done" and my queue is empty, the whole process exits. If I ctrl-F5 it, it does keep the process alive, but it never gets past the threadCurrent.Join() and the process stays alive forever. Keep in mind that my worker thread is a FOREGROUND thread. That is required to keep the process alive.

                D Offline
                D Offline
                Dave Kreskowiak
                wrote on last edited by
                #7

                OK, I second the remark that what you're saying isn't true. You're going to have to back up what you're saying with the code because I think you're misinterpreting what the code is really doing.

                A guide to posting questions on CodeProject

                Click this: Asking questions is a skill. Seriously, do it.
                Dave Kreskowiak

                S 1 Reply Last reply
                0
                • D Dave Kreskowiak

                  OK, I second the remark that what you're saying isn't true. You're going to have to back up what you're saying with the code because I think you're misinterpreting what the code is really doing.

                  A guide to posting questions on CodeProject

                  Click this: Asking questions is a skill. Seriously, do it.
                  Dave Kreskowiak

                  S Offline
                  S Offline
                  SledgeHammer01
                  wrote on last edited by
                  #8

                  If you say so :)... but here you go... here's a simple console app that demonstrates it. Run this IN THE DEBUGGER (F5) and the process will exit after 5 seconds. Now run it again with CTRL-F5 (NO DEBUGGER ATTACHED) and you'll see the process never exits.

                  using System;
                  using System.Collections.Generic;
                  using System.Linq;
                  using System.Text;
                  using System.Threading;
                  using System.Threading.Tasks;

                  namespace ConsoleApplication2
                  {
                  class Program
                  {
                  static bool b = false;

                  	static void Main(string\[\] args)
                  	{
                  		Thread threadCurrent = Thread.CurrentThread;
                  
                  		Task.Run(() =>
                  		{
                  			threadCurrent.Join();
                  			b = true;
                  		});
                  
                  		Thread \_threadWorker = new Thread(new ThreadStart(Worker));
                  		\_threadWorker.SetApartmentState(ApartmentState.STA);
                  		\_threadWorker.Start();
                  
                  		Thread.Sleep(5000);
                  	}
                  
                  	private static void Worker()
                  	{
                  		while (true)
                  		{
                  			Thread.Sleep(100);
                  
                  			if (b)
                  				break;
                  		}
                  	}
                  }
                  

                  }

                  D P L 3 Replies Last reply
                  0
                  • S SledgeHammer01

                    If you say so :)... but here you go... here's a simple console app that demonstrates it. Run this IN THE DEBUGGER (F5) and the process will exit after 5 seconds. Now run it again with CTRL-F5 (NO DEBUGGER ATTACHED) and you'll see the process never exits.

                    using System;
                    using System.Collections.Generic;
                    using System.Linq;
                    using System.Text;
                    using System.Threading;
                    using System.Threading.Tasks;

                    namespace ConsoleApplication2
                    {
                    class Program
                    {
                    static bool b = false;

                    	static void Main(string\[\] args)
                    	{
                    		Thread threadCurrent = Thread.CurrentThread;
                    
                    		Task.Run(() =>
                    		{
                    			threadCurrent.Join();
                    			b = true;
                    		});
                    
                    		Thread \_threadWorker = new Thread(new ThreadStart(Worker));
                    		\_threadWorker.SetApartmentState(ApartmentState.STA);
                    		\_threadWorker.Start();
                    
                    		Thread.Sleep(5000);
                    	}
                    
                    	private static void Worker()
                    	{
                    		while (true)
                    		{
                    			Thread.Sleep(100);
                    
                    			if (b)
                    				break;
                    		}
                    	}
                    }
                    

                    }

                    D Offline
                    D Offline
                    Dave Kreskowiak
                    wrote on last edited by
                    #9

                    Very interesting. Well, I verified it. That's the most screwed up thing ever. But, I hate to tell you this, but the one running under the debugger is lying to you. It's the one that's screwed up, not the one executing out of the debugger. For some reason, the debugger is keeping the Task alive when it shouldn't be. Tasks are running out of the thread pool, which are all background threads. When your main thread exited, it took the Task with it, killing the threadCurrent.Join and never allowing it to get to b = true. That's why your worker thread stays alive. I'd convert the Task to a foreground Thread instead. [EDIT] Oh, I almost forgot! You might want to post this on Connect[^] for a possible Visual Studio bug.

                    A guide to posting questions on CodeProject

                    Click this: Asking questions is a skill. Seriously, do it.
                    Dave Kreskowiak

                    L S 2 Replies Last reply
                    0
                    • S SledgeHammer01

                      If you say so :)... but here you go... here's a simple console app that demonstrates it. Run this IN THE DEBUGGER (F5) and the process will exit after 5 seconds. Now run it again with CTRL-F5 (NO DEBUGGER ATTACHED) and you'll see the process never exits.

                      using System;
                      using System.Collections.Generic;
                      using System.Linq;
                      using System.Text;
                      using System.Threading;
                      using System.Threading.Tasks;

                      namespace ConsoleApplication2
                      {
                      class Program
                      {
                      static bool b = false;

                      	static void Main(string\[\] args)
                      	{
                      		Thread threadCurrent = Thread.CurrentThread;
                      
                      		Task.Run(() =>
                      		{
                      			threadCurrent.Join();
                      			b = true;
                      		});
                      
                      		Thread \_threadWorker = new Thread(new ThreadStart(Worker));
                      		\_threadWorker.SetApartmentState(ApartmentState.STA);
                      		\_threadWorker.Start();
                      
                      		Thread.Sleep(5000);
                      	}
                      
                      	private static void Worker()
                      	{
                      		while (true)
                      		{
                      			Thread.Sleep(100);
                      
                      			if (b)
                      				break;
                      		}
                      	}
                      }
                      

                      }

                      P Offline
                      P Offline
                      PIEBALDconsult
                      wrote on last edited by
                      #10

                      I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.

                      D S 2 Replies Last reply
                      0
                      • D Dave Kreskowiak

                        Very interesting. Well, I verified it. That's the most screwed up thing ever. But, I hate to tell you this, but the one running under the debugger is lying to you. It's the one that's screwed up, not the one executing out of the debugger. For some reason, the debugger is keeping the Task alive when it shouldn't be. Tasks are running out of the thread pool, which are all background threads. When your main thread exited, it took the Task with it, killing the threadCurrent.Join and never allowing it to get to b = true. That's why your worker thread stays alive. I'd convert the Task to a foreground Thread instead. [EDIT] Oh, I almost forgot! You might want to post this on Connect[^] for a possible Visual Studio bug.

                        A guide to posting questions on CodeProject

                        Click this: Asking questions is a skill. Seriously, do it.
                        Dave Kreskowiak

                        L Offline
                        L Offline
                        Lost User
                        wrote on last edited by
                        #11

                        Tried the code this morning, first thing I did was change it to a foreground-thread. Strangely enough that made no difference at all. I can imagine that the main-thread isn't terminated, but stopped by the debugger. That would explain why the background-task that should have terminated didn't.

                        Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]

                        S 1 Reply Last reply
                        0
                        • P PIEBALDconsult

                          I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.

                          D Offline
                          D Offline
                          Dave Kreskowiak
                          wrote on last edited by
                          #12

                          It does seem like he's relying on a quirkish feature, doesn't it?

                          A guide to posting questions on CodeProject

                          Click this: Asking questions is a skill. Seriously, do it.
                          Dave Kreskowiak

                          S 1 Reply Last reply
                          0
                          • D Dave Kreskowiak

                            Very interesting. Well, I verified it. That's the most screwed up thing ever. But, I hate to tell you this, but the one running under the debugger is lying to you. It's the one that's screwed up, not the one executing out of the debugger. For some reason, the debugger is keeping the Task alive when it shouldn't be. Tasks are running out of the thread pool, which are all background threads. When your main thread exited, it took the Task with it, killing the threadCurrent.Join and never allowing it to get to b = true. That's why your worker thread stays alive. I'd convert the Task to a foreground Thread instead. [EDIT] Oh, I almost forgot! You might want to post this on Connect[^] for a possible Visual Studio bug.

                            A guide to posting questions on CodeProject

                            Click this: Asking questions is a skill. Seriously, do it.
                            Dave Kreskowiak

                            S Offline
                            S Offline
                            SledgeHammer01
                            wrote on last edited by
                            #13

                            Thank you for your attempt at an explanation of this bizarre behavior, but unfortunately, its not quite accurate :). I will show you why with two further examples :). First off, we can all agree that Worker is a foreground thread and that it keeps the process alive regardless of what the main threads state is, right? When the PROCESS exits, it will abort any BACKGROUND threads abruptly. We can all agree on that too. Since Worker is blocking the process from exiting, there is no reason for any background threads to get killed since the process is still alive. EXAMPLE #1:

                            using System;
                            using System.Collections.Generic;
                            using System.Linq;
                            using System.Text;
                            using System.Threading;
                            using System.Threading.Tasks;

                            namespace ConsoleApplication2
                            {
                            class Program
                            {
                            static bool b = false;

                            	static void Main(string\[\] args)
                            	{
                            		Thread threadCurrent = Thread.CurrentThread;
                            
                            		Task.Run(() =>
                            		{
                            			while (threadCurrent.IsAlive)
                            			{
                            				System.Diagnostics.Debug.WriteLine("MAIN THREAD IS ALIVE");
                            				Console.WriteLine("MAIN THREAD IS ALIVE");
                            				Thread.Sleep(250);
                            			}
                            
                            			System.Diagnostics.Debug.WriteLine("MAIN THREAD IS DEAD");
                            			Console.WriteLine("MAIN THREAD IS DEAD");
                            
                            			b = true;
                            		});
                            
                            		Thread \_threadWorker = new Thread(new ThreadStart(Worker));
                            
                            		\_threadWorker.SetApartmentState(ApartmentState.STA);
                            		\_threadWorker.Start();
                            
                            		Thread.Sleep(5000);
                            	}
                            
                            	private static void Worker()
                            	{
                            		while (true)
                            		{
                            			Thread.Sleep(100);
                            
                            			if (b)
                            			{
                            				Console.WriteLine("2) MAIN THREAD IS DEAD");
                            				break;
                            			}
                            		}
                            	}
                            }
                            

                            }

                            With this example, we can clearly see that the Task is not getting killed (or at least it appears that way) because everything works as expected. You get "MAIN THREAD IS ALIVE" messages for 5 seconds and then you get a "MAIN THREAD IS DEAD" message AND the "2) MAIN THREAD IS DEAD" message from the worker thread. Everything worked as expected and all messages were printed as expected and everything exited cleanly. A race condition perhaps? Perhaps... Perhaps not :). I've never gotten any of the messages not to print with this implementation, but lets bump up the 250ms sleep to 3000ms and try that... Nope... still works as expected, everything behaves correctly :). EXAMPLE #2: This is my favorite one :)... I'm just replacing the Task code from the code above with this Task implementation:

                            Task.Run(() =>
                            {
                            //while (threadCurrent.IsAlive)
                            //{

                            1 Reply Last reply
                            0
                            • L Lost User

                              Tried the code this morning, first thing I did was change it to a foreground-thread. Strangely enough that made no difference at all. I can imagine that the main-thread isn't terminated, but stopped by the debugger. That would explain why the background-task that should have terminated didn't.

                              Bastard Programmer from Hell :suss: If you can't read my code, try converting it here[^]

                              S Offline
                              S Offline
                              SledgeHammer01
                              wrote on last edited by
                              #14

                              Oh, I was going to try make that a foreground thread... so thanks for saving me the trouble :). Check out my response to Dave that further proves his explanation is not correctly. The main jist of the two further examples I posted was to prove that the Task is most certainly not getting killed and I wouldn't expect it to since the process is still alive.

                              1 Reply Last reply
                              0
                              • P PIEBALDconsult

                                I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.

                                S Offline
                                S Offline
                                SledgeHammer01
                                wrote on last edited by
                                #15

                                Do you also need to tell your watch dog to bark and attack the guy breaking into your house in the middle of the night? :). The whole point of the watch dog thread is to encapsulate expected behavior. Expected behavior being that as a developer, if I send 100 items into the queue (an API), I would expect all 100 to get processed without me having to implement a "wait for the queue to be done" event of some sort. I want that to be encapsulated into the API magically. What I'm getting at is, the Worker thread is automatically created when the application starts (as part of my API), I thought it'd be kind of cheezy to have to make the developer remember to call theAPI.Shutdown()... That'd be like Microsoft making you add an Exit() call at the end of your main() in a console app.

                                P 1 Reply Last reply
                                0
                                • D Dave Kreskowiak

                                  It does seem like he's relying on a quirkish feature, doesn't it?

                                  A guide to posting questions on CodeProject

                                  Click this: Asking questions is a skill. Seriously, do it.
                                  Dave Kreskowiak

                                  S Offline
                                  S Offline
                                  SledgeHammer01
                                  wrote on last edited by
                                  #16

                                  I think what I'm trying to do is pretty reasonable. Worker is actually a service (think service in the sense of Dependency Injection or Service Locator pattern) that services a queue. I don't know when (or if) messages will get thrown into the queue, so the queue needs to be monitored for the life of the process (its a blocking queue). If Worker was a background task, I would either need the app to implement some kind of "wait for all the messages to be processed" behavior or have messages lost. In fact, if I make the Worker a background thread and have the console app send a 1000 messages into the queue, the process would exit after only 1 or 2 of the messages were serviced. NOT DESIRABLE. So while I stripped all that out of my examples, the real intention is for the watch dog task to wait for the main thread to shutdown AND all the remaining messages in the queue to be processed and then set "b = true" so the worker thread can shut down (which makes the process shutdown cleanly). Not too unreasonable, huh? :) Like I said in another response (I think to Eddy)... when I make reusable APIs, I like to encapsulate all the behavior / requirements / code I can into the class so the user of my API can use it in a simple way.

                                  D 1 Reply Last reply
                                  0
                                  • S SledgeHammer01

                                    Do you also need to tell your watch dog to bark and attack the guy breaking into your house in the middle of the night? :). The whole point of the watch dog thread is to encapsulate expected behavior. Expected behavior being that as a developer, if I send 100 items into the queue (an API), I would expect all 100 to get processed without me having to implement a "wait for the queue to be done" event of some sort. I want that to be encapsulated into the API magically. What I'm getting at is, the Worker thread is automatically created when the application starts (as part of my API), I thought it'd be kind of cheezy to have to make the developer remember to call theAPI.Shutdown()... That'd be like Microsoft making you add an Exit() call at the end of your main() in a console app.

                                    P Offline
                                    P Offline
                                    PIEBALDconsult
                                    wrote on last edited by
                                    #17

                                    Have a WatchDog object that implements IDisposable. Done.

                                    S 2 Replies Last reply
                                    0
                                    • P PIEBALDconsult

                                      Have a WatchDog object that implements IDisposable. Done.

                                      S Offline
                                      S Offline
                                      SledgeHammer01
                                      wrote on last edited by
                                      #18

                                      Who creates the watch dog object? The developer? Or the API?

                                      1 Reply Last reply
                                      0
                                      • P PIEBALDconsult

                                        Have a WatchDog object that implements IDisposable. Done.

                                        S Offline
                                        S Offline
                                        SledgeHammer01
                                        wrote on last edited by
                                        #19

                                        How would that work anyways? The user of the API would have to manually create the watch dog object in some class that lives in the main thread, correct? (if I'm understanding your idea). That's even uglier then calling theAPI.Shutdown().

                                        P 1 Reply Last reply
                                        0
                                        • S SledgeHammer01

                                          How would that work anyways? The user of the API would have to manually create the watch dog object in some class that lives in the main thread, correct? (if I'm understanding your idea). That's even uglier then calling theAPI.Shutdown().

                                          P Offline
                                          P Offline
                                          PIEBALDconsult
                                          wrote on last edited by
                                          #20

                                          Then shouldn't the API be IDisposable?

                                          S 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups