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类型的泛型是没有问题的。问题是似乎没有任何方法可以将事件传递给方法,因此