如何等待C#事件被提升?
我有一个Sender
类,它在IChannel
上发送一条Message
:
public class MessageEventArgs : EventArgs { public Message Message { get; private set; } public MessageEventArgs(Message m) { Message = m; } } public interface IChannel { public event EventHandler MessageReceived; void Send(Message m); } public class Sender { public const int MaxWaitInMs = 5000; private IChannel _c = ...; public Message Send(Message m) { _c.Send(m); // wait for MaxWaitInMs to get an event from _c.MessageReceived // return the message or null if no message was received in response } }
当我们发送消息时, IChannel
有时会根据通过引发MessageReceived
事件发送的Message
类型给出响应。 事件参数包含感兴趣的消息。
我希望Sender.Send()
方法等待很短的时间来查看是否引发了此事件。 如果是这样,我将返回其MessageEventArgs.Message
属性。 如果没有,我返回一个空Message
。
我怎么能这样等? 我宁愿不使用ManualResetEvents
等进行线程化工作,因此坚持常规event
对我来说是最佳选择。
使用AutoResetEvent
。
给我几分钟,然后我会把样品扔到一起。
这里是:
public class Sender { public static readonly TimeSpan MaxWait = TimeSpan.FromMilliseconds(5000); private IChannel _c; private AutoResetEvent _messageReceived; public Sender() { // initialize _c this._messageReceived = new AutoResetEvent(false); this._c.MessageReceived += this.MessageReceived; } public Message Send(Message m) { this._c.Send(m); // wait for MaxWaitInMs to get an event from _c.MessageReceived // return the message or null if no message was received in response // This will wait for up to 5000 ms, then throw an exception. this._messageReceived.WaitOne(MaxWait); return null; } public void MessageReceived(object sender, MessageEventArgs e) { //Do whatever you need to do with the message this._messageReceived.Set(); } }
您是否尝试将函数指定为异步调用委托,然后调用mydelegateinstance.BeginInvoke?
林基供参考。
使用下面的示例,只需调用即可
FillDataSet(ref table, ref dataset);
它会像魔法一样工作。 🙂
#region DataSet manipulation ///Fills a the distance table of a dataset private void FillDataSet(ref DistanceDataTableAdapter taD, ref MyDataSet ds) { using (var myMRE = new ManualResetEventSlim(false)) { ds.EnforceConstraints = false; ds.Distance.BeginLoadData(); Func distanceFill = taD.Fill; distanceFill.BeginInvoke(ds.Distance, FillCallback, new object[] { distanceFill, myMRE }); WaitHandle.WaitAll(new []{ myMRE.WaitHandle }); ds.Distance.EndLoadData(); ds.EnforceConstraints = true; } } /// /// Callback used when filling a table asynchronously. /// /// Represents the status of the asynchronous operation. private void FillCallback(IAsyncResult result) where MyDataTable: DataTable { var state = result.AsyncState as object[]; Debug.Assert((state != null) && (state.Length == 2), "State variable is either null or an invalid number of parameters were passed."); var fillFunc = state[0] as Func; var mre = state[1] as ManualResetEventSlim; Debug.Assert((mre != null) && (fillFunc != null)); int rowsAffected = fillFunc.EndInvoke(result); Debug.WriteLine(" Rows: " + rowsAffected.ToString()); mre.Set(); }
在实现INotifyPropertyChanged事件处理程序时,您的MessageReceived方法可能只需将值标记为IChannel接口的属性,以便在更改属性时通知您。
通过这样做,您的Sender类可以循环,直到最大等待时间过去,或者每当PropertyChanged事件处理程序发生时,成功地中断循环。 如果你的循环没有被破坏,那么该消息将被视为从未收到过。
AutoResetEvent的有用示例:
using System; using System.Threading; class WaitOne { static AutoResetEvent autoEvent = new AutoResetEvent(false); static void Main() { Console.WriteLine("Main starting."); ThreadPool.QueueUserWorkItem( new WaitCallback(WorkMethod), autoEvent); // Wait for work method to signal. autoEvent.WaitOne(); Console.WriteLine("Work method signaled.\nMain ending."); } static void WorkMethod(object stateInfo) { Console.WriteLine("Work starting."); // Simulate time spent working. Thread.Sleep(new Random().Next(100, 2000)); // Signal that work is finished. Console.WriteLine("Work ending."); ((AutoResetEvent)stateInfo).Set(); } }
WaitOne
确实是这项工作的正确工具。 简而言之,您希望在0到MaxWaitInMs
毫秒之间等待作业完成。 你真的有两个选择,轮询完成或者用一些可以等待任意时间的构造同步线程。
既然你很清楚正确的方法,那么对于后人,我会发布投票版本:
MessageEventArgs msgArgs = null; var callback = (object o, MessageEventArgs args) => { msgArgs = args; }; _c.MessageReceived += callback; _c.Send(m); int msLeft = MaxWaitInMs; while (msgArgs == null || msLeft >= 0) { Thread.Sleep(100); msLeft -= 100; // you should measure this instead with say, Stopwatch } _c.MessageRecieved -= callback;