views:

321

answers:

3

I have a method which calls an asynchronous method, and a callback which is fired when the asynchronous method finishes.

I want my method to appear syncrhonous, so I've created an AutoResetEvent, called the asyncrhonous method, the call WaitOne() on the AutoResetEvent instance, and I call Set() in the callback method. Something like this (simplified for this example):

private System.Threading.AutoResetEvent waitRun_m;
public void RunSynchronous()
{
    waitRun_m = new System.Threading.AutoResetEvent(false);
    CallAsynchronousMethod();

    waitRun_m.WaitOne();
}

private void Callback()
{
    waitRun_m.Set();
}

Now, is it possible for the call to CallAsynchronousMethod to complete before WaitOne() to be called - causing Set() to be called before WaitOne(). Is there a better way to do this, to avoid this potential issue?

+3  A: 

It is not an issue, the event getting Set before you WaitOne is fully supported. AutoResetEvent would be quite unusable if this wasn't the case.

Hans Passant
But if Set was called first, then WaitOne would end up causing the thread to block endifinitely...
Jeremy
If Set is called first, everything will be fine. WaitOne will return immediately.
Daniel Yankowsky
No. It is the WaitOne() call that resets it, not the thread exit.
Hans Passant
+4  A: 

I believe this will answer your question:

Calling Set signals AutoResetEvent to release a waiting thread. AutoResetEvent remains signaled until a single waiting thread is released, and then automatically returns to the non-signaled state. If no threads are waiting, the state remains signaled indefinitely.

You have to be careful, though, because now RunSynchronous doesn't look thread safe. If two different threads call it in an overlapping manner, all hell could break loose.

Daniel Yankowsky
A: 

As Daniel and nobugz said using AutoResetEvent might be dangerous. You might be calling waitRun_m.Set(); before you call waitRun_m.WaitOne(); when an async operation is very short. I'd prefer something like this. That way you are sure that you will first enter the wait state, and then call the Pulse method. Plus you don't have to Close the AutoResetEvent which is often forgoten.

private readonly object m_lock = new object();
public void RunSynchronous()
{
    lock(m_lock) {
        CallAsynchronousMethod();
        Monitor.Wait(m_lock);
    }
}

private void Callback()
{
    lock(m_lock)
        Monitor.Pulse(m_lock);
}
kossib
Err, I was saying that AutoResetEvent itself is OK. The Set/WaitOne ordering will take care of itself, as the MSDN quote says. What I was objecting to is that the AutoResetEvent is a field, that field is reassigned every time that RunSynchronous is called, and that means that RunSynchronous itself is not safe to be called by multiple threads at the same time. Your solution also looks like it would work, but it doesn't make RunSynchronous any more thread-safe.
Daniel Yankowsky