|
|
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.
|
|
|
|
|
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.
|
|
|
|
|
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 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.
|
|
|
|
|
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
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.
|
|
|
|
|
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(() =>
{
System.Diagnostics.Debug.WriteLine("WAITING FOR MAIN THREAD TO DIE");
Console.WriteLine("WAITING FOR MAIN THREAD TO DIE");
threadCurrent.Join(6000);
System.Diagnostics.Debug.WriteLine("MAIN THREAD IS DEAD");
Console.WriteLine("MAIN THREAD IS DEAD");
b = true;
});
Yes, a contrived example, but it clearly proves that the Task is not getting killed outside of the debugger since after the 6s timeout, the Task is still alive .
Any ideas? I will try using a foreground thread for the watch dog, but your original explanation doesn't explain what we're seeing with these two examples .
I certainly would NOT expect a task to get taken out by the main thread exiting. Background threads only get killed when there are no more foreground threads. There is one in this case: the Worker.
|
|
|
|
|
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?
|
|
|
|
|
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.
|
|
|
|
|
I didn't say that wasn't unreasonable.
What I said was relying on a quirk of background/forground threads.
What you're scribing, I would probably use either events or other callbacks to signal completions.
I wouldn't have the main thread terminating and then waiting for a "background" thread to terminate itself. I'd keep the main thread around waiting for the background to finish so the main thread can receive notification that it is done and can keep other things alive, such logging API's or other things.
|
|
|
|
|
It's actually a pretty similar scenario to a logging API, an asynchronous one. Think about Log4Net or NLog or whatever. They have an email listener for example. That's going to be "slow". But regardless, lets say your console app writes 1000 log entries to the log, the Log() method is going to return right away because its async. So the main thread is going to be done and exit after like a second, but its going to take the logger a minute or two or whatever to send all those emails. Yes, I could have a design like:
theLogger.Flush(); / theLogger.Shutdown();
and have that method block the main thread until its done.
or I can do what you said:
theLogger.OnComplete += blah...
and have the blah method block the main process. But I see a few issues with both of those methods IMHO
1) Just like my queue implementation, a logger queue isn't going to have log entries to write all the time... how is the logger to know when to send the OnComplete() notification? Lets say you send it every time the queue goes empty... ok, well, now you require even more work on the developers part as they have to know if this OnComplete() event is the one they really care about and then they have to couple that with some mechanism to block the thread. Then as the developer of the API, you're going to get blamed cuz the developer didn't implement it right!
2) the .Flush() / .Shutdown() / etc. is cleaner and much less error prone, but this requires the developer to remember to call .Flush() / .Complete() and of course, you'll get blamed for a "buggy framework" when they forget because "your framework should handle this automatically -- hanging is a 'bug'".
3) finally, and this kind of a nitpick on my part... I see a logger (and my queue) as a fire & forget thing... I don't care about getting logging notifications back, I don't care about whether the logging queue is empty or not, I don't want to concern myself with any of that. I simply want to call Log("whatever") and move on with my life. Same thing with my queue.
This is what I'm trying to implement here... a mechanism that'll shut down the queue automatically.
|
|
|
|
|
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.
|
|
|
|
|
Who creates the watch dog object? The developer? Or the API?
|
|
|
|
|
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?
|
|
|
|
|
Mind you, I'm not trying to argue with you (just in case you are starting to get annoyed lol)...
The API is implemented as a service (in the DI / SL sense of the word service, not as a Windows Service), so the instance of my API ends up being a static singleton instance inside of the DI container. It wouldn't get disposed unless the user manually called Dispose on the container. Calling Dispose() on the container is only a requirement if the container contains objects with unmanaged resources that need to be freed up. That is *very* rare in my world as 99.99999% of our stuff is strictly managed.
I know I'm sounding picky by not wanting to add any additional burden on developers that I don't need to, but you'd have to understand the type of people I work with to understand why... . Like the type of people who milk a literal 5 minute task for 6 months to a year type of people. Like the type of people who rather then fix memory leaks in applications simply write scripts to reboot the machine daily type of people .
|
|
|
|
|
Isn't a thread an unmanaged resource?
|
|
|
|
|
You mean as in System.Threading.Thread and Tasks? Well, personally, I'm conscientious about cleaning up everything I create, but threads and tasks clean themselves up when they exit. I was more referring to stuff that would cause a memory leak or OS instability if you didn't free it up like say COM objects or unmanaged bitmaps, that sort of thing.
But to your original point, my worker thread is specifically a foreground thread because I WANT it to "hang" / keep the process alive until its done.
I guess what you're saying is that technically this would qualify for my earlier requirement of only forcing the guy to call Dispose() on the container if something needs to be cleaned up, right?
Yeah, I guess... except in my head, I'm differentiating between stock services that are part of my API and magically registered automatically vs. any services the developer registers .
Maybe I shouldn't make that differentiation, but in my company, its perfectly acceptable to blame your co-worker for their screw up in using your framework, better then you getting crap for your "buggy framework"
So that's really why I'm being so picky in trying to handle as much as I can in my API. So any bugs I can just throw back at the developer that he didn't use my framework right. That's how my company works unfortunately.
|
|
|
|
|
Task.Run create a new thread on the threadpool, a backgroundworker that runs in its own thread. That will block until it can join with the main-thread(*!), and die as soon as the main-thread dies (after five seconds), and Joins on the mainthread. It is that task that I moved to a foreground thread, not the worker.
*! that means you wait until the proces exits and this task is stopped. B should never become true.
SledgeHammer01 wrote: Background threads only get killed when there are no more foreground threads. There is one in this case: the Worker. Where does it say that in the documentation? AFAIK, the behaviour of the background thread is not guaranteed when the proces (the main thread) is terminated.
http://msdn.microsoft.com/en-us/library/system.threading.thread.isbackground(v=vs.100).aspx[^]
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
It says it right in the documentation you linked:
Quote: Background threads are identical to foreground threads, except that background threads do not prevent a process from terminating
In plain English, background threads do NOT block the process from terminating while foreground threads DO.
Quote: Once all foreground threads belonging to a process have terminated, the common language runtime ends the process. Any remaining background threads are stopped and do not complete.
Says right here that as soon as all foreground threads are finished, the process exits. Background threads are terminated on the spot.
I don't know if you read every post in this thread, but there was one where I posted additional proof of it being broken and that the task is staying around as it should:
http://www.codeproject.com/Messages/4940135/Re-Waiting-for-thread-to-exit.aspx[^]
|
|
|
|