Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
171 views
in Technique[技术] by (71.8m points)

c# - Blocking and waiting for an event

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...