如何等待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;