我有一个IObservable<Packet>
这是一个热门观察,它允许不同的订阅来分析传入的数据包。
我想编写一个方法,使用和ID发送一些数据,并等待具有相同ID的响应。伪代码:
void SendData(byte[] data, int retries, int timeout, Action<Packet> success, Action fail)
{
var sequenceId = GetSequenceId();
_port.SendData(data, sequenceId);
_packetStream.Where(p => p.SequenceId == sequenceId)
.Take(1)
.WaitForTimeout(timeout)
.WaitForRetry(retries)
.Subscribe(success) //Need to unsubscribe after packet is received
//If we didn't receive an answer packet, then call fail() action
}
不知道,这些东西通常是如何通过Reactive Extensions完成的。很高兴收到一些建议。感谢。
答案 0 :(得分:1)
您问题中的代码看起来很接近。 Rx框架中存在两个“等待”方法(Timeout和Retry)。我建议您更改方法以返回IObservable
并删除success
和fail
参数。这样做“让你保持在monad”,并允许你根据需要将更多的操作符链接到observable。当您订阅生成的observable(分别为OnNext和OnError)时,将使用成功和失败参数。
我假设数据应该在超时时重新发送(否则你并没有真正重试)。为此,您可以使用Observable.Create
在订阅时发送数据。
IObservable<Packet> SendData(byte[] data, int retries, TimeSpan timeout)
{
//only get the sequence id once per call to SendData, regardless of retries
var sequenceId = GetSequenceId();
return Observable.Create(obs =>
{ //this code runs every time you subscribe
_port.SendData(data, sequenceId);
return _packetStream.Where(p => p.SequenceId == sequenceId)
.Take(1)
.Timeout(timeout)
.Subscribe(obs)
})
.Retry(retries);
}
将Retry
运算符放在最后会导致创建observable超时时重试。顺便说一句,有超时的重载允许您传入另一个可观察的序列,以便在超时的情况下使用。如果需要,您可以将此重载与Observable.Throw
一起使用以提供自己的异常,例如提供备用错误消息。
请注意,此代码不会发送数据,直到您订阅并且不阻止,直到返回结果或达到超时但允许您取消进一步的重试处理订阅。此代码也不会阻止您同时发送多个数据包。如果您必须阻止,您可以执行以下操作:
var response = SendData(/* arguments */);
response.Do(success, fail).StartWith(null).ToTask().Wait();
如果您使用的是C#5并在异步方法中调用它,则可以等待observable。
答案 1 :(得分:0)
到目前为止我得到的是:
private void WaitForAnswer(byte sequenceId, int timeout, Action<Packet> success, Action fail, int retriesLeft)
{
_packetStream.Where(p => p.GetSequence() == sequenceId)
.Take(1)
.Timeout(TimeSpan.FromMilliseconds(timeout))
.Subscribe(success,
_ => {
if (retriesLeft > 0) WaitForAnswer(sequenceId, timeout, success, fail, --retriesLeft);
else fail();
},
() => { });
}
我不太确定,如果此解决方案正确处理订阅。