10

Consider the following pattern:

private AutoResetEvent signal = new AutoResetEvent(false);

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        signal.Set();
        //has a waiting thread definitely been signaled by now?
        signal.Reset();
    }
}

public void WaitForNextEvent()
{
    signal.WaitOne();
}

The purpose of this pattern is to allow external consumers to wait for a certain event (e.g. - a message arriving). WaitForNextEvent is not called from within the class.

To give an example that should be familiar, consider System.Diagnostics.Process. It exposes an Exited event, but it also exposes a WaitForExit method, which allows the caller to wait synchronously until the process exits. this is what I am trying to achieve here.

The reason I need signal.Reset() is that if a thread calls WaitForNextEvent after signal.Set() has already been called (or in other words, if .Set was called when no threads were waiting), it returns immediately, as the event has already been previously signaled.

The question

  • Is it guaranteed that a thread calling WaitForNextEvent() will be signaled before signal.Reset() is called? If not, what are other solutions for implementing a WaitFor method?
Community
  • 1
  • 1
Rotem
  • 21,452
  • 6
  • 62
  • 109
  • It isn't clear (at least for me) what you are trying to do here. Could you explain what you would like the threads flow to be? – xanatos Sep 04 '13 at 08:54
  • 2
    An `AutoResetEvent` automatically resets after one thread has successfully waited for it via `WaitOne()` so you should *not* need to call `signal.Reset()`. – Matthew Watson Sep 04 '13 at 08:54
  • Yes, it's the same with a ManualResetEvent (see [this question from me](http://stackoverflow.com/questions/15067369/manualreseteventslim-calling-set-followed-immediately-by-reset-doesnt-re)). However, the big issue for me is why the OP thinks he needs to call `.Reset()` for an `AutoResetEvent`... – Matthew Watson Sep 04 '13 at 09:17
  • I think you're using the wrong synchronization primitive. If you could describe what behaviour you're aiming for in a bit more detail, we might be able to help out. E.g. a [`Barrier`](http://msdn.microsoft.com/en-us/library/system.threading.barrier.aspx) lets a specific number of threads all rendezvous and then move forward into a new phase together. – Damien_The_Unbeliever Sep 04 '13 at 09:33
  • I've updated my question to better explain my intent. – Rotem Sep 04 '13 at 09:49
  • Why not use a semaphore instead, (System.Threading.Semaphore)? It maintains state - a count, and so it would not matter if the signal occurred before the wait. – Martin James Sep 04 '13 at 09:52
  • @MartinJames I'm not sure I understand how a semaphore would help me here. The intended behavior is that a thread calling `WaitOne` **after** `Set` has been called will block until the next `Set` is called. Could you give an example of how that would work with a semaphore? – Rotem Sep 04 '13 at 10:02
  • @Rotem - uhh.. OK. I was a little unclear on your exact requirement so, luckily, I commented instead of answering:) – Martin James Sep 04 '13 at 10:05
  • I was going to write an answer that involved using a `TaskCompletionSource` - swapping an old one with a new one before setting the result on the old one. But then realised that if multiple threads were waiting on one, then they would *all* be released, whereas with the `AutoResetEvent`, only *one* thread can be released. – Damien_The_Unbeliever Sep 04 '13 at 10:15
  • I'll say that the example you give and the objective you have are totally different. `To give an example that should be familiar, consider System.Diagnostics.Process` Here the event can happen only once. The Process has Exited or it hasn't. After it has exited it can't "restart" and "reexit". The example you gave was a tight cycle with a lock/unlock after each cycle. – xanatos Sep 04 '13 at 10:38
  • @Damien_The_Unbeliever But the example he gives now, the `WaitForExit `, all the threads waiting will be started after the exit :-) – xanatos Sep 04 '13 at 10:39
  • @xanatos - you're write. For some reason I had it in mind that an `AutoResetEvent` would only release one thread. – Damien_The_Unbeliever Sep 04 '13 at 10:47
  • @Damien_The_Unbeliever You were not wrong about that. From http://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.set.aspx : **"For an EventWaitHandle with EventResetMode.AutoReset (including AutoResetEvent), the Set method releases a single thread."** – Rotem Sep 04 '13 at 11:01
  • The `Process` example is completely different because it is only signalled once. Neither `ManualResetEvent` nor `AutoResetEvent` will work in this situation - [they can't be "pulsed" reliably](http://blogs.msdn.com/b/oldnewthing/archive/2005/01/05/346888.aspx). Matthew's answer uses `Monitor` which correctly avoids this problem. – Stephen Cleary Sep 04 '13 at 11:55

4 Answers4

8

Instead of using AutoResetEvent or ManualResetEvent, use this:

public sealed class Signaller
{
    public void PulseAll()
    {
        lock (_lock)
        {
            Monitor.PulseAll(_lock);
        }
    }

    public void Pulse()
    {
        lock (_lock)
        {
            Monitor.Pulse(_lock);
        }
    }

    public void Wait()
    {
        Wait(Timeout.Infinite);
    }

    public bool Wait(int timeoutMilliseconds)
    {
        lock (_lock)
        {
            return Monitor.Wait(_lock, timeoutMilliseconds);
        }
    }

    private readonly object _lock = new object();
}

Then change your code like so:

private Signaller signal = new Signaller();

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        signal.Pulse(); // Or signal.PulseAll() to signal ALL waiting threads.
    }
}

public void WaitForNextEvent()
{
    signal.Wait();
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
2

There is no guarantee. This:

AutoResetEvent flag = new AutoResetEvent(false);

new Thread(() =>
{
    Thread.CurrentThread.Priority = ThreadPriority.Lowest;
    Console.WriteLine("Work Item Started");
    flag.WaitOne();
    Console.WriteLine("Work Item Executed");
}).Start();

// For fast systems, you can help by occupying processors.
for (int ix = 0; ix < 2; ++ix)
{
    new Thread(() => { while (true) ; }).Start();
}

Thread.Sleep(1000);
Console.WriteLine("Sleeped");

flag.Set();
// Decomment here to make it work
//Thread.Sleep(1000);

flag.Reset();
Console.WriteLine("Finished");
Console.ReadLine();

won't print "Work Item Executed" on my system. If I add a Thread.Sleep between the Set and the Reset it prints it. Note that this is very processor dependent, so you could have to create tons of threads to "fill" the CPUs. On my PC it's reproducible 50% of the times :-)

For the Exited:

readonly object mylock = new object();

then somewhere:

lock (mylock)
{
    // Your code goes here
}

and the WaitForExit:

void WaitForExit()
{
    lock (mylock) ;
    // exited
}

void bool IsExited()
{
    bool lockTacken = false;

    try
    {
        Monitor.TryEnter(mylock, ref lockTacken);
    }
    finally
    {
        if (lockTacken)
        {
            Monitor.Exit(mylock);
        }
    }

    return lockTacken;
}

Note that the lock construct isn't compatible with async/await (as aren't nearly all the locking primitives of .NET)

xanatos
  • 109,618
  • 12
  • 197
  • 280
1

I would use TaskCompletionSources:

private volatile TaskCompletionSource<int> signal = new TaskCompletionSource<int>();

private void Work()
{
    while (true)
    {
        Thread.Sleep(5000);
        var oldSignal = signal;
        signal = new TaskCompletionSource<int>()
        //has a waiting thread definitely been signaled by now?
        oldSignal.SetResult(0);
    }
}

public void WaitForNextEvent()
{
    signal.Task.Wait();
}

By the time that the code calls SetResult, no new code entering WaitForNextEvent can obtain the TaskCompletionSource that is being signalled.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
0

I believe it is not guaranteed.

However, your logic flow is not understood by me. If your main thread Sets the signal, why should it wait until that signal reaches its destination? Wouldn't it be better to continue your "after signal set" logic in that thread which was waiting?

If you cannot do that, I recommend you to use second WaitHandle to signal the first thread that the second one has reveiced the signal. But I cannot see any pros of such a strategy.

AgentFire
  • 8,944
  • 8
  • 43
  • 90