У меня есть 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
и такой, как придерживание регулярного event
s был бы оптимален для меня.
Используйте 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
///<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();
}
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;
Возможно, ваш метод MessageReceived должен просто отмечать значение свойства вашего интерфейса IChannel, реализуя при этом обработчик события INotifyPropertyChanged, чтобы вы получали уведомление об изменении свойства.
Сделав это, ваш класс Sender может зациклиться до истечения максимального времени ожидания, или когда произойдет событие PropertyChanged, обработчик которого успешно прервет цикл. Если цикл не будет разорван, то сообщение будет считаться неполученным.