Как я ожидаю события C#, которое будет повышено?

У меня есть a Sender класс, который отправляет a Message на a 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 и такой, как придерживание регулярного events был бы оптимален для меня.

9
задан Evan Barkley 12 May 2010 в 16:37
поделиться

4 ответа

Используйте 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();
    }
}
15
ответ дан 4 December 2019 в 13:45
поделиться

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

Linky для справки.

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

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();
}
1
ответ дан 4 December 2019 в 13:45
поделиться

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;
-1
ответ дан 4 December 2019 в 13:45
поделиться

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

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

0
ответ дан 4 December 2019 в 13:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: