阻止和等待事件 [英] Blocking and waiting for an event

查看:86
本文介绍了阻止和等待事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我通常会这样做:

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

然而,似乎这应该是我可以提取到一个常见的类(或者甚至是框架中已经存在的东西)。



我想要这样做:

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

但是我找不到一种构建这样的类的方法(我找不到将事件作为参数传递的良好有效方法)。有人可以帮忙吗?



举个例子说明为什么这可以有用,请考虑一下:

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

这是比许多方法之间扩展的逻辑写的等效代码更加简洁易读。但是要实现WaitForUsbStickOrCancel(),WaitForUsbStickToBeRemoved和WaitUntilUserPressesDone()(假设我们在插入或删除USB插件时收到一个事件),我需要每次重新实现EventWaiter。当然,你必须小心,不要在GUI线程上运行,但是有时这对于更简单的代码是有价值的。



替代方案看起来像这个:

  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 * /

我发现读取难度很大。诚然,流量将是如此直线,但是,当它是,我喜欢第一种风格是非常不错的。



它与使用ShowMessage()和Form.ShowDialog() - 它们也阻塞直到发生一些事件(尽管如果在gui线程上调用它们将运行一个消息循环)。

解决方案

我修改了Dead.Rabit的类EventWaiter来处理 EventHandler< T> 。所以你可以使用等待所有事件类型的 EventHandler< T> ,这意味着你的委托是像code> 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);
}
}

例如我用它来等待获取URL当我注册到Windows推送通知服务时,从HttpNotificationChannel。

  HttpNotificationChannel pushChannel = new HttpNotificationChannel(channelName); 
// ChannelUriUpdated是事件
EventWaiter< NotificationChannelUriEventArgs> ew = new EventWaiter< NotificationChannelUriEventArgs>(pushChannel,ChannelUriUpdated);
pushChannel.Open();
ew.WaitForEvent(TimeSpan.FromSeconds(30));


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

解决方案

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

这篇关于阻止和等待事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆