我有一个从外部类接收标准.Net事件的类。 这些事件有一个地址属性(当然还有很多其他属性)我可以用它来同步我的事件,这样我就可以创建一个获取东西的方法,等待正确的事件,然后返回Get方法中事件的数据。
但是,我对C#中的同步还是比较陌生的,并且希望你们中的任何一个能帮助我。下面是我想要完成的伪代码:
DoAsynchronousToSynchronousCall
DoAsynchronousToSynchronousCall
知道回复已到达DoAsynchronousCall
获取(或检索)回复并将其返回给调用者public class MyMessage
{
public string Address { get; set; }
public string Data { get; set; }
}
public Main
{
externalClass.MessageReceived += MessageReceived;
}
public void MessageReceived(MyMessage message)
{
MyMessage request = _requestQueue.FirstOrDefault(m => m.Address = message.Address);
if (request != null)
{
// Do something to let DoAsynchronousToSynchronousCall() know the reply has arrived
}
}
private List<MyMessage> _requestQueue = new List<MyMessage>();
public MyMessage DoAsynchronousToSynchronousCall(MyMessage message)
{
_requestQueue.Add(message);
externalClass.Send(message);
// Do something to wait for a reply (as checked for above)
MyMessage reply = WaitForCorrectReply(timeout: 10000);
return reply;
}
我觉得我错过了使用async
和await
的机会(但我不知道怎么做),我希望你能够理解我根据上述信息尝试完成的任务。
答案 0 :(得分:0)
您实际上无法动态拨打多个电话并拥有同步响应。如果您想要多个呼叫的同步响应,那么您也需要同步进行呼叫。
我会考虑使用Microsoft的Reactive Extensions(NuGet“Rx-Main”)来尽可能简化您正在做的事情。 Rx允许您将事件转换为可以查询的值流。
这就是我要做的事。
我首先将收到的消息流定义为IObservable<MyMessage> receivedMessages
,如下所示:
receivedMessages =
Observable
.FromEvent<MessageReceivedHandler, MyMessage>(
h => externalClass.MessageReceived += h,
h => externalClass.MessageReceived -= h);
(你没有提供一个def类,所以我调用了事件委托MessageReceivedHandler
。)
现在您可以将DoAsynchronousToSynchronousCall
重新定义为:
public IObservable<MyMessage> DoAsynchronousCall(MyMessage message)
{
return Observable.Create<MyMessage>(o =>
{
IObservable<MyMessage> result =
receivedMessages
.Where(m => m.Address == message.Address)
.Take(1);
IObservable<MyMessage> timeout =
Observable
.Timer(TimeSpan.FromSeconds(10.0))
.Select(x => (MyMessage)null);
IDisposable subscription =
Observable
.Amb(result, timeout)
.Subscribe(o);
externalClass.Send(message);
return subscription;
});
}
result
可观察量是针对当前receivedMessages
过滤的message.Address
。
如果调用时间超过timeout
,TimeSpan.FromSeconds(10.0)
observable将返回默认值。
最后,subscription
使用Observable.Amb(...)
来确定result
或timeout
中的哪一个首先产生值并订阅该结果。
现在打电话给你,你可以这样做:
DoAsynchronousCall(new MyMessage() { Address = "Foo", Data = "Bar" })
.Subscribe(response => Console.WriteLine(response.Data));
所以,如果我像这样简单地定义ExternalClass
:
public class ExternalClass
{
public event MessageReceivedHandler MessageReceived;
public void Send(MyMessage message)
{
this.MessageReceived(new MyMessage()
{
Address = message.Address,
Data = message.Data + "!"
});
}
}
...我在控制台上打印出结果Bar!
。
如果您要处理大量消息,可以执行以下操作:
var messagesToSend = new List<MyMessage>();
/* populate `messagesToSend` */
var query =
from message in messagesToSend.ToObservable()
from response in DoAsynchronousCall(message)
select new
{
message,
response
};
query
.Subscribe(x =>
{
/* Do something with each correctly paired
`x.message` & `x.response`
*/
});
答案 1 :(得分:-1)
你可能正在寻找ManualResetEvent
,它可以作为各种类型的“切换”来切换线程阻塞和非阻塞行为。 DoAsynchronousToSynchronousCall
会Reset
然后WaitOne(int timeoutMilliseconds)
阻止线程的事件,并且检查到的正确回复的内容将执行Set
调用以让线程继续如果正确的事情到来的话。