Thread.Join appears to return false incorrectly
c#, multithreading
Solution
If you want to be sure that the appdomains always unload within 5 seconds, you can try to measure it. For example using something like this:
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
AppDomain.Unload(someAppDomain);
long elapsedMillis = stopwatch.ElapsedMilliseconds;
System.Diagnostics.Trace.Writeline("Unload duration: " + elapsedMillis + " ms");
The Output window of Visual Studio (or the DebugView tool from sysinternals) should show it
Problem
I am using `Thread.Join(int millisecondsTimeout)` to terminate a number of `AppDomain`s. Frequently, I get an error message stating that the AppDomain did not terminate within 5 seconds. Whilst stepping through the debugger I see that the `AppDomain.Unload()` call terminates easily within 5 seconds, but `Thread.Join` returns false. Where am I going wrong? ``` var thread = new Thread( () => { try { AppDomain.Unload(someAppDomain); } catch (ArgumentNullException) { } catch (CannotUnloadAppDomainException exception) { // Some error message } }); thread.Start(); const int numSecondsWait = 5; if (!thread.Join(1000 * numSecondsWait)) { // Some error message about it not exiting in 5 seconds } ``` Edit 1 Worth adding what each of the `AppDomain`s do. Each `AppDomain` has at least one `Timer`. The code roughly looks as follows, (keep in mind I've collapsed loads of classes into one here for readability). ``` static void Main(string[] args) { _exceptionThrown = new EventWaitHandle(false, EventResetMode.AutoReset); _timer = new Timer(TickAction, null, 0, interval); try { _exceptionThrown.WaitOne(); } finally { _timer.Dispose(_timerWaitHandle); WaitHandle.WaitAll(_timerWaitHandle); } } ``` In effect I know that the "Main" thread will throw a `ThreadAbortException`, jump into the finally statement and ensure the `Timer` queue is fully drained before exiting. All of the `Timer`s though log when they are inside the tick method. So I can be near certain that there is nothing on the timer queue, and the `_timer.Dispose(_timerWaitHandle)` returns immediately. Regardless of whether it does or not, at least one of the three `AppDomain`s I am `Unload`ing will not complete it within 5 seconds.