Waiting for thread to exit?
-
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.
-
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.
Will Thread.Join work better? http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx[^]
-
Will Thread.Join work better? http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx[^]
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.
-
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.
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[^]
-
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.
SledgeHammer01 wrote:
only works on the main thread if the debugger is attached
That is definitely untrue. I use Join all the time.
-
SledgeHammer01 wrote:
only works on the main thread if the debugger is attached
That is definitely untrue. I use Join all the time.
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.
-
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.
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 -
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 KreskowiakIf 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; } } }
}
-
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; } } }
}
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 tob = 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 -
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; } } }
}
I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.
-
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 tob = 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 KreskowiakTried 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[^]
-
I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.
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 -
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 tob = 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 KreskowiakThank 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)
//{ -
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[^]
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.
-
I think you have it backward. The main thread should wait on the background/worker thread, not the other way around.
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.
-
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 KreskowiakI 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.
-
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.
Have a WatchDog object that implements IDisposable. Done.
-
Have a WatchDog object that implements IDisposable. Done.
Who creates the watch dog object? The developer? Or the API?
-
Have a WatchDog object that implements IDisposable. Done.
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().
-
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().
Then shouldn't the API be IDisposable?