Как мне ждать события C #? - PullRequest
9 голосов
/ 12 мая 2010

У меня есть класс 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 ]

15 голосов
/ 12 мая 2010

Используйте 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 голос
/ 12 мая 2010

Вы пытались присвоить делегату функцию асинхронного вызова, а затем вызывать mydelegateinstance.BeginInvoke?

Ссылка для справки.

В приведенном ниже примере просто позвоните

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();
}
0 голосов
/ 06 октября 2014

Полезный образец с 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();
        }
    }
0 голосов
/ 12 мая 2010

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;
0 голосов
/ 12 мая 2010

Возможно, ваш метод MessageReceived должен просто пометить значение свойством интерфейса IChannel при реализации обработчика события INotifyPropertyChanged, чтобы вам сообщали об этом при изменении свойства.

Таким образом, ваш класс Sender может зацикливаться до тех пор, пока не истечет максимальное время ожидания или всякий раз, когда происходит обработчик события PropertyChanged, успешно прерывая цикл. Если ваш цикл не прерывается, то сообщение считается не полученным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...