C# 阻止并等待事件

C# 阻止并等待事件,c#,.net,events,C#,.net,Events,它有时想在等待事件发生时阻止我的线程 我通常是这样做的: private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); private void OnEvent(object sender, EventArgs e){ _autoResetEvent.Set(); } // ... button.Click += OnEvent; try{ _autoResetEvent.WaitOne(); } finall

它有时想在等待事件发生时阻止我的线程

我通常是这样做的:

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();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
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 */
然而,这似乎应该是我可以提取到一个公共类中的东西(或者甚至是框架中已经存在的东西)

我希望能够做到以下几点:

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();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
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 */
但是我真的找不到一种方法来构造这样一个类(我找不到一种有效的方法来将事件作为参数传递)。有人能帮忙吗

为了举例说明为什么这是有用的,考虑这样的事情:

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();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
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 */
这比使用分散在多个方法之间的逻辑编写的等效代码更简洁易读。但要实现WaitForUsbStickOrCancel()、WaitForUsbStickToBeRemoved和WaitUntilUserPressesDone()(假设在插入或移除U盘时我们会收到一个事件),我每次都需要重新实现“EventWater”。当然,您必须小心,永远不要在GUI线程上运行它,但有时对于更简单的代码来说,这是一个值得权衡的问题

可供选择的方案如下所示:

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();
if(!cancelled){
  status.ShowWritingOnUsbStick();
  WriteOnUsbStick();
  status.AskUserToRemoveUsbStick();
  WaitForUsbStickToBeRemoved();
  status.ShowFinished();
}else{
  status.ShowCancelled();
}
status.WaitUntilUserPressesDone();
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线程上调用它们,它们将运行消息循环)。

如果不传递事件,请传递与事件处理程序签名匹配的委托。对我来说,这听起来有点骇人听闻,所以要注意潜在的死锁问题。

我认为这些应该可以工作,而不是仅仅尝试编码

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();
    }
}
公共类eventwater,其中T:EventArgs
{
专用System.Threading.ManualResetEvent手动事件;
公共活动服务员(T e)
{
manualEvent=新系统。螺纹。ManualResetEvent(错误);
e+=这个.OnEvent;
}
public void OnEvent(对象发送方,事件参数)
{
manualEvent.Set();
}
公营机构
{
manualEvent.WaitOne();
}
公共无效重置()
{
manualEvent.Reset();
}
}
没有考虑太多,但不知道如何使其与EventArgs隔离


查看一下,您会发现您可以将等待和一些奇怪的东西链接起来。

我使用反射在LinqPad中快速创建了一个工作示例,用一个字符串获取对EventInfo对象的引用(请小心,不要放松编译时检查)。显而易见的问题是,没有任何保证事件会被触发,或者您期望的事件可能会在EventWater类准备好开始阻止之前被触发,因此如果我将其放入生产应用程序中,我不确定我是否会睡得舒服

void Main()
{
    Console.WriteLine( "main thread started" );

    var workerClass = new WorkerClassWithEvent();
    workerClass.PerformWork();

    var waiter = new EventWaiter( workerClass, "WorkCompletedEvent" );
    waiter.WaitForEvent( TimeSpan.FromSeconds( 10 ) );

    Console.WriteLine( "main thread continues after waiting" );
}

public class WorkerClassWithEvent
{
    public void PerformWork()
    {
        var worker = new BackgroundWorker();
        worker.DoWork += ( s, e ) =>
        {
            Console.WriteLine( "threaded work started" );
            Thread.Sleep( 1000 ); // <= the work
            Console.WriteLine( "threaded work complete" );
        };
        worker.RunWorkerCompleted += ( s, e ) =>
        {
            FireWorkCompletedEvent();
            Console.WriteLine( "work complete event fired" );
        };

        worker.RunWorkerAsync();
    }

    public event Action WorkCompletedEvent;
    private void FireWorkCompletedEvent()
    {
        if ( WorkCompletedEvent != null ) WorkCompletedEvent();
    }
}

public class EventWaiter
{
    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 )
    {
        _event.AddEventHandler( _eventContainer, (Action)delegate { _autoResetEvent.Set(); } );
        _autoResetEvent.WaitOne( timeout );
    }
}

我修改了Dead.Rabit的类eventwater来处理
EventHandler
。因此,您可以使用等待所有事件类型的
EventHandler
,这意味着您的委托类似于
delegate void SomeDelegate(objectsender,T EventsArgs)

公共类事件服务程序
{
私有自动重置事件_AutoResetEvent=新自动重置事件(false);
私有事件信息_event=null;
私有对象_eventContainer=null;
public eventwater(对象eventContainer,字符串eventName)
{
_eventContainer=eventContainer;
_event=eventContainer.GetType().GetEvent(eventName);
}
公共无效WaitForEvent(TimeSpan超时)
{
EventHandler EventHandler=neweventhandler((发送方,参数)=>{{u autoResetEvent.Set();});
_AddEventHandler(_eventContainer,eventHandler);
_autoResetEvent.WaitOne(超时);
_event.RemoveEventHandler(_eventContainer,eventHandler);
}
}
例如,当我注册到windows推送通知服务时,我使用它来等待从HttpNotificationChannel获取Url

            HttpNotificationChannel pushChannel = new HttpNotificationChannel(channelName);
            //ChannelUriUpdated is event 
            EventWaiter<NotificationChannelUriEventArgs> ew = new EventWaiter<NotificationChannelUriEventArgs>(pushChannel, "ChannelUriUpdated");
            pushChannel.Open();
            ew.WaitForEvent(TimeSpan.FromSeconds(30));
HttpNotificationChannel pushChannel=新的HttpNotificationChannel(channelName);
//ChannelUriIS事件已更新
EventWater ew=新的EventWater(推送频道,“频道更新”);
pushChannel.Open();
ew.WaitForEvent(时间跨度从秒(30));
您也可以尝试以下方法:

class EventWaiter<TEventArgs> where TEventArgs : EventArgs
{
    private readonly Action<EventHandler<TEventArgs>> _unsubHandler;
    private readonly Action<EventHandler<TEventArgs>> _subHandler;

    public EventWaiter(Action<EventHandler<TEventArgs>> subHandler, Action<EventHandler<TEventArgs>> unsubHandler)
    {
        _unsubHandler = unsubHandler;
        _subHandler = subHandler;
    }

    protected void Handler(object sender, TEventArgs args)
    {
        _unsubHandler.Invoke(Handler);
        TaskCompletionSource.SetResult(args);
    }

    public  TEventArgs WaitOnce()
    {
        TaskCompletionSource = new TaskCompletionSource<TEventArgs>();
        _subHandler.Invoke(Handler);
        return TaskCompletionSource.Task.Result;
    }

    protected TaskCompletionSource<TEventArgs> TaskCompletionSource { get; set; } 

}
类eventwater,其中TEventArgs:EventArgs
{
私有只读操作_unsbhandler;
私人只读操作_subHandler;
公共事件服务员(行动副手、行动副手)
{
_Unsbhandler=Unsbhandler;
_副手=副手;
}
受保护的无效处理程序(对象发送方、TEventArgs参数)
{
_unsubHandler.Invoke(处理程序);
TaskCompletionSource.SetResult(args);
}
公共TEventArgs WaitOnce()
{
TaskCompletionSource=新的TaskCompletionSource();
_调用(处理程序);
返回TaskCompletionSource.Task.Result;
}
受保护的TaskCompletionSource TaskCompletionSource{get;set;}
}
用法:

EventArgs eventArgs = new EventWaiter<EventArgs>((h) => { button.Click += new EventHandler(h); }, (h) => { button.Click -= new EventHandler(h); }).WaitOnce();
EventArgs EventArgs=neweventwater((h)=>{button.Click+=neweventhandler(h);},(h)=>{button.Click-=neweventhandler(h);}).WaitOnce();

我不确定我是否理解。你的意思是传递订阅和取消订阅活动的代理(例如,new EventWater(eh=>{button.Click+=eh;},eh=>{button.Click-=eh;}),我想这会有用,但我更喜欢更简单的方法。我很好奇你到底为什么要这样做。。。你能详细说明一下吗?我仍然不明白为什么你想阻止线程而不是仅仅等待事件-这能完成什么?你有没有解决过这个问题?我也遇到了同样的问题。@Carlo:对不起,没有。我认为没有好的解决方案。这太糟糕了=(,感谢您的快速响应!EventArgs并不是一个真正的问题。将它作为EventArgs类型的泛型是没有问题的。问题是似乎没有任何方法可以将事件传递给方法,因此