tags:

views:

2087

answers:

3

It sometimes want to block my thread while waiting for a event to occur.

I usually do it something like this:

private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

private void OnEvent(object sender, EventArgs e){
  _autoResetEvent.Set();
}

// ...
button.Click += OnEvent;
try{
  _autoResetEvent.WaitOne();
}
finally{
  button.Click -= OnEvent;
}

However, it seems that this should be something that I could extract to a common class (or perhaps even something that already exists in the framework).

I would like to be able to do something like this:

EventWaiter ew = new EventWaiter(button.Click);
ew.WaitOne();
EventWaiter ew2 = new EventWaiter(form.Closing);
ew2.WaitOne();

But I can't really find a way to construct such a class (I can't find a good valid way to pass the event as an argument). Can anyone help?

To give an example of why this can be useful, consider something like this:

var status = ShowStatusForm();
status.ShowInsertUsbStick();
bool cancelled = WaitForUsbStickOrCancel();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();

This is much more concise and readable than the equivalent code written with the logic spread out between many methods. But to implement WaitForUsbStickOrCancel(), WaitForUsbStickToBeRemoved and WaitUntilUserPressesDone() (assume that the we get an event when usb sticks are inserted or removed) I need to reimplement "EventWaiter" each time. Of course you have to be careful to never run this on the GUI-thread, but sometimes that is a worthwhile tradeoff for the simpler code.

The alternative would look something like this:

var status = ShowStatusForm();
status.ShowInsertUsbStick();
usbHandler.Inserted += OnInserted;
status.Cancel += OnCancel;
//...
void OnInserted(/*..*/){
  usbHandler.Inserted -= OnInserted;
  status.ShowWritingOnUsbStick();
  MethodInvoker mi = () => WriteOnUsbStick();
  mi.BeginInvoke(WritingDone, null);
}
void WritingDone(/*..*/){
  /* EndInvoke */
  status.AskUserToRemoveUsbStick();
  usbHandler.Removed += OnRemoved;
}
void OnRemoved(/*..*/){
  usbHandler.Removed -= OnRemoved;
  status.ShowFinished();
  status.Done += OnDone;
}
/* etc */

I find that much harder to read. Admittedly, it is far from always that the flow will be so linear, but when it is, I like the first style.

It is comparable to using ShowMessage() and Form.ShowDialog() - they also block until some "event" occurs (though they will run a message-loop if they are called on the gui-thread).

+2  A: 

Don't pass the event, pass a delegate that matches the event handler signature. This actually sounds hacky to me, so be aware of potential dead lock issues.

Ricardo Villamil
I am not sure I understand. Do you mean passing delegates that subscribes and unsubscribes from the event (ie. new EventWaiter( eh => { button.Click += eh; }, eh => { button.Click -= eh; } ) ) That would work, I suppose, but I would prefer something simpler.
Rasmus Faber
A: 

Hi, try this ManualResetEvent

BrutusCat
How would that help?
Rasmus Faber
Take a look at the next answer ;)
BrutusCat
Yeah I saw that too.
Daniel Schaffer
Ok, can you help him instead of vote down and do nothing? or say what is wrong with the code?
BrutusCat
I'm waiting for him to respond to my comment on the question explain why he's trying to do this, because I can't think of why anyone would want to purposefully block their thread like this.
Daniel Schaffer
A: 

I think like these should work, didn't tried just coded.

public class EventWaiter<T> where T : EventArgs
{
    private System.Threading.ManualResetEvent manualEvent;

    public EventWaiter(T e)
    {
        manualEvent = new System.Threading.ManualResetEvent(false);
        e += this.OnEvent;
    }

    public void OnEvent(object sender, EventArgs e)
    {
        manualEvent.Set();
    }

    public void WaitOne()
    {
        manualEvent.WaitOne();
    }

    public void Reset()
    {
        manualEvent.Reset();
    }
}

Didn't thought about too much, but can't figure out how to make it isolated from the EventArgs.

Take a look at the MSDN ManualResetEvent and you will discover that you can kind of chain the waits and so some weird stuff.

BrutusCat
It is not really a problem with the EventArgs. It would be no problem to make it generic regarding the EventArgs-type. The problem is that there do not seem to be any way to pass an event to a method, so that you can attach an event-handler (it is not EventArgs you attach to as in your example).
Rasmus Faber
OMG!! Yes you're right I don't know what I was thinking! :S
BrutusCat