How do I unblock threads which have called the WaitOne method on an AutoResetEvent object?
Asked Answered
H

3

12

Below is a class having the method 'SomeMethod' that illustrates my problem.

class SomeClass
{
    AutoResetEvent theEvent = new AutoResetEvent(false);
    // more member declarations

    public void SomeMethod()
    {
        // some code
        theEvent.WaitOne();
        // more code
    }
}

The method is designed to be thread-safe and will be called in different threads. Now my question is how is it possible to unblock all threads that have called the 'WaitOne' method on the 'theEvent' object at any point of time? This requirement frequently arises in my design because I need to be able to gracefully stop and start my multi-threaded program. It seems to me that it's fairly simple to start a multi-threaded program, but tricky to stop one.

Here's what I have tried so far which apparently works. But is this the standard approach?

public void UnblockAll()
{
    do
    {
        theEvent.Set();
    } while (theEvent.WaitOne(0));
}

The 'UnblockAll' method is a member of the 'SomeClass' class. The technique used here is based on the MSDN documentation of the WaitOne method. I am quoting the relevant part of the documentation below:

If millisecondsTimeout is zero, the method does not block. It tests the state of the wait handle and returns immediately.

In the do..while loop, I call the Set method. This releases a single thread that may have blocked due to a call to the WaitOne method (coded inside the 'SomeMethod' method). Next I test the state of the 'theEvent' object just to know whether it's signalled. This test is done by calling the overloaded version of the WaitOne method that takes a time out parameter. The argument which I use when I call the WaitOne method is zero, which as per the documentation results in the call returning immediately with a boolean value. If the return value is true, then the 'theEvent' object was in a signalled state. If there was at least a single thread blocked on the call to the 'WaitOne' method in the 'SomeMethod' method, the call to the 'Set' method (coded inside the 'UnblockAll' method) would unblock it. Consequently the call to the 'WaitOne' method at the end of the do..while statement in the 'UnblockAll' method would return false. The return value is true only if there were no threads blocked.

Is the above reasoning right and if it's right, is the technique a standard way to deal with my problem? I am trying to use the solution primarily on the .net compact-framework 2.0 platform.

Harlen answered 9/5, 2011 at 16:56 Comment(3)
Off topic - You can't call variable - 'event'Lox
Sounds like you want a ManualResetEvent instead. This way it will stay set even if you have multiple listeners.Quacksalver
@Maxim. corrected the syntax errors in the code..Harlen
M
11

You have three viable options. Each one has its own advantages and disadvantages. Pick the one that works best for your specific situation.

Option 1 - Poll the WaitHandle.

Instead of doing an indefinite blocking call use one with a timeout and reinstate the block if a shutdown request has not been given.

public void SomeMethod()
{
  while (!yourEvent.WaitOne(POLLING_INTERVAL))
  {
    if (IsShutdownRequested())
    {
      // Add code to end gracefully here.
    }
  }
  // Your event was signaled so now we can proceed.
}

Option 2 - Use a separate WaitHandle for requesting shutdown

public void SomeMethod()
{
  WaitHandle[] handles = new WaitHandle[] { yourEvent, shutdownEvent };
  if (WaitHandle.WaitAny(handles) == 1)
  {
    // Add code to end gracefully here.
  }
  // Your event was signaled so now we can proceed.
}

Option 3 - Use Thread.Interrupt

Do not confuse this with Thread.Abort. Aborting a thread is definitely unsafe, but interrupting a thread is completely different. Thread.Interrupt will "poke" the builtin blocking calls used in the BCL including Thread.Join, WaitHandle.WaitOne, Thread.Sleep, etc.

Mai answered 9/5, 2011 at 17:49 Comment(3)
.Net compact framework 2.0 doesn't have the WaitAny method in its WaitHandle class :-(Harlen
Too bad. By the way, I forget to mention another option. I edited my answer.Mai
Interrupting is not safe if you call into code you do not own. You might interrupt other peoples waits.Elvieelvin
E
3

Your routine will probably work most of the time, but I don't think there's any guarantee that one of the waiting threads will reset the event between the time that your shutdown loop sets it and the time that your shutdown loop checks it again.

I find that the AutoResetEvent and ManualResetEvent classes work great for really simple scenarios. Any time there's anything odd about the requirements, I quickly switch to the more flexible Wait And Pulse pattern.

If you don't require any clean up, you could make your worker threads background threads, and then they'll just stop when the main thread exits.

You might also be able to define a second ManualResetEvent called stopRequest and wait on a signal from either event. However, that might not be supported on the compact framework.

Entablement answered 9/5, 2011 at 17:9 Comment(2)
There's no Wait-Pulse methods available in the .Net compact framework 2.0 platform :-(Harlen
The whole program is organized into a set of cooperating 'components'. Some of the components are used by multiple threads. I feel that unless I provide a means to gracefully shutdown a component without adversely affecting other components, my design is not complete. Hence making the threads background is my last option.Harlen
I
1

Does thread abort work for your framework?

Infract answered 9/5, 2011 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.