我如何等待C#事件被提升?

时间:2010-05-12 16:37:31

标签: c# events

我有一个Sender课程,在Message上发送IChannel

public class MessageEventArgs : EventArgs {
  public Message Message { get; private set; }
  public MessageEventArgs(Message m) { Message = m; }
}

public interface IChannel {
  public event EventHandler<MessageEventArgs> 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有时会根据提出Message事件发送的MessageReceived类型给出回复。事件参数包含感兴趣的消息。

我希望Sender.Send()方法等待一小段时间以查看是否引发了此事件。如果是这样,我将返回其MessageEventArgs.Message属性。如果没有,我返回一个空Message

我怎么能以这种方式等待?我宁愿不使用ManualResetEvents这样的线程练习,所以坚持常规event对我来说是最佳的。

5 个答案:

答案 0 :(得分:15)

使用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();
    }
}

答案 1 :(得分:1)

您是否尝试过分配函数以异步方式调用委托,然后调用mydelegateinstance.BeginInvoke?

Linky for reference.

使用以下示例,只需调用

即可
FillDataSet(ref table, ref dataset);

它会像神奇的一样起作用。 :)

#region DataSet manipulation
///<summary>Fills a the distance table of a dataset</summary>
private void FillDataSet(ref DistanceDataTableAdapter taD, ref MyDataSet ds) {
  using (var myMRE = new ManualResetEventSlim(false)) {
    ds.EnforceConstraints = false;
    ds.Distance.BeginLoadData();
    Func<DistanceDataTable, int> distanceFill = taD.Fill;
    distanceFill.BeginInvoke(ds.Distance, FillCallback<DistanceDataTable>, new object[] { distanceFill, myMRE });
    WaitHandle.WaitAll(new []{ myMRE.WaitHandle });
    ds.Distance.EndLoadData();
    ds.EnforceConstraints = true;
  }
}
/// <summary>
/// Callback used when filling a table asynchronously.
/// </summary>
/// <param name="result">Represents the status of the asynchronous operation.</param>
private void FillCallback<MyDataTable>(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<MyDataTable, int>;
  var mre = state[1] as ManualResetEventSlim;
  Debug.Assert((mre != null) && (fillFunc != null));
  int rowsAffected = fillFunc.EndInvoke(result);
  Debug.WriteLine(" Rows: " + rowsAffected.ToString());
  mre.Set();
}

答案 2 :(得分:0)

在实现INotifyPropertyChanged事件处理程序时,也许您的MessageReceived方法应该只是将值标记为IChannel接口的属性,以便在更改属性时通知您。

通过这样做,您的Sender类可以循环,直到最大等待时间结束,或者每当PropertyChanged事件处理程序发生时,成功地中断循环。如果您的循环没有被破坏,那么该消息将被视为从未收到过。

答案 3 :(得分:0)

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

答案 4 :(得分:-1)

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;