Blocking and waiting for an event

后端 未结 5 1625
不知归路
不知归路 2020-12-15 03:45

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

I usually do it something like this:

private AutoResetEvent _autoResetEvent          


        
相关标签:
5条回答
  • 2020-12-15 04:22

    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.

    0 讨论(0)
  • 2020-12-15 04:23

    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.

    0 讨论(0)
  • 2020-12-15 04:23

    I've rushed together a working sample in LinqPad using reflection, getting a reference to the EventInfo object with a string (be careful as you loose compile time checking). The obvious issue is that there is no guarentee an event will ever be fired, or that the event your expecting may be fired before the EventWaiter class is ready to start blocking so I'm not sure I'd sleep comfy if I put this in a production app.

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

    Output

    // main thread started
    // threaded work started
    // threaded work complete
    // work complete event fired
    // main thread continues after waiting
    
    0 讨论(0)
  • 2020-12-15 04:24

    You may also try this:

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

    Usage:

    EventArgs eventArgs = new EventWaiter<EventArgs>((h) => { button.Click += new EventHandler(h); }, (h) => { button.Click -= new EventHandler(h); }).WaitOnce();
    
    0 讨论(0)
  • 2020-12-15 04:28

    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));
    
    0 讨论(0)
提交回复
热议问题