1

I'm playing around with AutoResetEvent and my app isn't ending, and I think I know why: the threads are still running and therefore the app won't terminate. Normally, in Main(), after I press a key, the app terminates. But the console window no longer closes. I have a simple console app:

    private static EventWaitHandle waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        AutoResetEventFun();

        Console.WriteLine("Press any key to end.");
        Console.ReadKey();

        waitHandle.Close();  // This didn't cause the app to terminate.
        waitHandle.Dispose();  // Nor did this.
    }

    private static void AutoResetEventFun()
    {
        // Start all of our threads.
        new Thread(ThreadMethod1).Start();
        new Thread(ThreadMethod2).Start();
        new Thread(ThreadMethod3).Start();
        new Thread(ThreadMethod4).Start();

        while (Console.ReadKey().Key != ConsoleKey.X)
        {
            waitHandle.Set();  // Let one of our threads process.
        }
    }

    // There are four of these methods. Only showing this one for brevity.
    private static void ThreadMethod1()
    {
        Console.WriteLine("ThreadMethod1() waiting...");

        while (true)
        {
            waitHandle.WaitOne();
            Console.WriteLine("ThreadMethod1() continuing...");   
        }
    }

What's the right way to terminate this app? Do I need to retain a reference to each thread and call Abort() on each one? Is there a way to signal waitHandle so that the threads waiting on it will terminate? (I don't think so, but I thought it would be worth asking.)

Bob Horn
  • 33,387
  • 34
  • 113
  • 219

2 Answers2

8

While I'm not entirely sure what you're trying to accomplish, one way to have this app terminate is to make all thread background threads:

private static void ThreadMethod1()
{ 
    Thread.CurrentThread.IsBackground = true;
    Console.WriteLine("ThreadMethod1() waiting...");

    while (true)
    {
        waitHandle.WaitOne();
        Console.WriteLine("ThreadMethod1() continuing...");   
    }
}
BFree
  • 102,548
  • 21
  • 159
  • 201
  • 1
    Simply trying to learn, that's all. :) And your suggestion worked. That seems like the way to go for this simple example. Thanks! – Bob Horn Sep 23 '12 at 03:47
  • 2
    No problem! We're all learning at one point :) I don't think you'll typically use these signaling mechanisms in threads that never terminate. I think a more common use case is when you have a thread do some work, but can't continue until something else happens elsewhere. Once that something happens, it resumes and finishes its work. The Background Thread solution is still good to know, it's an important aspect of Threads. One final thing, manually created Threads are Foreground by default, and ThreadPool threads are background by default. – BFree Sep 23 '12 at 03:52
  • I was following Joseph Albahari's Threading in C# article, and I wanted to try using `AutoResetEvent`. Then I searched for a practical reason to use it, and found this from Mr. Skeet: http://stackoverflow.com/questions/7735809/autoresetevent-and-manualresetevent. Thank you for the tips. Every bit helps... – Bob Horn Sep 23 '12 at 03:56
  • AutoResetEvent is like a semaphore that doesn't really work well if more than one thread can wait on it. It has no state, no count, and so signals can get lost, as you have found out:( – Martin James Sep 23 '12 at 20:28
  • Despite providing an alternative solution, I have upvoted this. If you can get away with it, allowing the OS to terminate threads upon app close is the only way to close a multithreaded app that is sure to work quickly and efficiently. I know that you can't always do this because the app requires some resource with thread-affinity to be flushed/closed but, then again, this is by no means the commonest case. – Martin James Sep 23 '12 at 20:32
1

Another way is to set a volatile 'Abort' boolean flag that the threads always check after returning from the WaitOne() call to see if they need to exit. You could then set this flag and signal the WaitHandle [no. of threads] times.

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • I like this approach because it gives the threads a chance to exit gracefully. However, in the main thread, I can't just call `waitHandle.Set()` four times and exit because the main thread then exits before *some* of threads have a chance to stop. The main thread needs to know when all the other threads are done before closing. +1 for a good tip, though. Thanks! – Bob Horn Sep 23 '12 at 12:54
  • I've been working on trying to get this right, and I ran into another issue. If I call waitHandle.Set() four times, it happens so quickly that it's possible for one thread to be affected by two Set() calls. That means another thread never has Set() applied to it. – Bob Horn Sep 23 '12 at 13:38
  • I solved this by looping through the threads. If any thread is alive, I call Set(), Thread.Sleep(100), and keep looping until no more threads are alive. So, good suggestion, but it was a little bit more complicated than simply calling Set() four times. – Bob Horn Sep 23 '12 at 13:42
  • OK, another alternative, use System.Threading.Semaphore instead of the AutoResetEvent, (which can be described as a crippled semaphore). – Martin James Sep 23 '12 at 20:21