我有一个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
对我来说是最佳的。
答案 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?
使用以下示例,只需调用
即可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;