Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blocking and waiting for an event

Tags:

c#

.net

events

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).

like image 397
Rasmus Faber Avatar asked Jan 29 '09 15:01

Rasmus Faber


1 Answers

I modified Dead.Rabit's class EventWaiter to handle EventHandler<T>. So you can use for waiting all events type of EventHandler<T>, that means your delegate is something like delegate void SomeDelegate(object sender, T EventsArgs).

 public class EventWaiter<T> {      private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);     private EventInfo _event = null;     private object _eventContainer = null;      public EventWaiter(object eventContainer, string eventName)     {         _eventContainer = eventContainer;         _event = eventContainer.GetType().GetEvent(eventName);     }      public void WaitForEvent(TimeSpan timeout)     {         EventHandler<T> eventHandler = new EventHandler<T>((sender, args) => { _autoResetEvent.Set(); });         _event.AddEventHandler(_eventContainer, eventHandler);         _autoResetEvent.WaitOne(timeout);         _event.RemoveEventHandler(_eventContainer, eventHandler);     } } 

And for example I use that for waiting to get Url from HttpNotificationChannel when I registering to windows push notification service.

            HttpNotificationChannel pushChannel = new HttpNotificationChannel(channelName);             //ChannelUriUpdated is event              EventWaiter<NotificationChannelUriEventArgs> ew = new EventWaiter<NotificationChannelUriEventArgs>(pushChannel, "ChannelUriUpdated");             pushChannel.Open();             ew.WaitForEvent(TimeSpan.FromSeconds(30)); 
like image 74
Kalanj Djordje Djordje Avatar answered Sep 28 '22 04:09

Kalanj Djordje Djordje