C# 如何通过RX实现超时顺序?
该场景如下:如果通过网络进行通信的设备在短时间内回调服务器,则认为该设备已连接。 我想创建一个类来封装跟踪此状态的功能。调用设备时,应重置超时。在回调时,连接被确认,状态应设置为true,如果回调超时,则应设置为false。但是下一次调用应该能够重新设置超时,而与当前状态无关 我想通过使用C# 如何通过RX实现超时顺序?,c#,system.reactive,.net-4.6,C#,System.reactive,.net 4.6,该场景如下:如果通过网络进行通信的设备在短时间内回调服务器,则认为该设备已连接。 我想创建一个类来封装跟踪此状态的功能。调用设备时,应重置超时。在回调时,连接被确认,状态应设置为true,如果回调超时,则应设置为false。但是下一次调用应该能够重新设置超时,而与当前状态无关 我想通过使用swith和timeout来实现这一点。但我不知道为什么它停止工作 public class ConnectionStatus { private Subject<bool> pending = n
swith
和timeout
来实现这一点。但我不知道为什么它停止工作
public class ConnectionStatus
{
private Subject<bool> pending = new Subject<bool>();
private Subject<bool> connected = new Subject<bool>();
public bool IsConnected { get; private set; }
public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
pending.Select(outer => connected.Timeout(TimeSpan.FromSeconds(timeoutSeconds)))
.Switch()
.Subscribe(_ => IsConnected = true, e => IsConnected = false, token);
}
public void ConfirmConnected()
{
connected.OnNext(true);
}
public void SetPending()
{
pending.OnNext(true);
}
}
我假设内部可观测的超时也停止了外部可观测的超时。因为不再调用outer=>
lambda。正确的方法是什么
谢谢问题在于,
超时
实质上会导致异常导致接收订阅中断。触发超时后(按照您的编码),将不会发送其他通知。Rx语法是,您可以在*OnNext
消息后加一个OnCompleted
或一个OnError
。发送Timeout
中的OnError
后,您将看不到更多消息
您需要通过OnNext
消息传递超时消息,而不是OnError
消息。在旧代码中,您将任何OnError
转换为false,将任何OnNext
转换为true。相反,您需要在OnNext
消息中嵌入适当的新IsConnected
值。以下是如何做到这一点:
public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
pending.Select(_ => connected
.Timeout(TimeSpan.FromSeconds(timeoutSeconds))
.Materialize()
.Select(n => n.Kind == NotificationKind.OnError && n.Exception.GetType() == typeof(TimeoutException)
? Notification.CreateOnNext(false)
: n)
.Dematerialize()
.Take(1)
)
.Switch()
.Subscribe(b => IsConnected = b, token);
}
这里有一种替代方法,可以在不使用
.TimeOut
的情况下生成IsConnected
值流:
public class ConnectionStatus
{
private Subject<Unit> pending = new Subject<Unit>();
private Subject<Unit> connected = new Subject<Unit>();
public bool IsConnected { get; private set; }
public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
pending
.Select(outer =>
Observable.Amb(
connected.Select(_ => true),
Observable.Timer(TimeSpan.FromSeconds(timeoutSeconds)).Select(_ => false)))
.Switch()
.Subscribe(isConnected => IsConnected = isConnected, token);
}
public void ConfirmConnected()
{
connected.OnNext(Unit.Default);
}
public void SetPending()
{
pending.OnNext(Unit.Default);
}
}
公共类连接状态
{
私有主题待定=新主题();
连接的私有主题=新主题();
公共布尔已连接{get;private set;}
公共连接状态(CancellationToken令牌,短时超时=15)
{
悬而未决的
.选择(外部=>
可观察的(
已连接。选择(=>true),
可观察的.Timer(TimeSpan.FromSeconds(timeoutSeconds))。选择(=>false)))
.Switch()
.Subscribe(isConnected=>isConnected=isConnected,令牌);
}
公共无效确认连接()
{
connected.OnNext(单位默认值);
}
public void SetPending()
{
未决.OnNext(单位默认值);
}
}
Observable.Amb
操作符只需从哪个Observable首先生成一个值的值中获取一个值-这比编码异常要好。谢谢。我已经测试了您的建议,发现它需要一个.FirstOrDefaultAsync()
在选择之后。如果没有它,即使已连接
输入了该值,也会触发超时。同时,我找到了另一个解决方案:pending.Select(=>connected.Buffer(TimeSpan.FromSeconds(timeoutSeconds),1.FirstOrDefaultAsync()).Switch().Subscribe(l=>IsConnected=l.Count>0,令牌)
在什么情况下您发现需要FirstOrDefaultAsync
?c.SetPending();等待任务延迟(时间跨度从秒(5));c.确认连接();c.IsConnected.Dump();等待任务延迟(时间跨度从秒(20));c.IsConnected.Dump()
后一个应该仍然为TRUE,但如果没有FirstOrDefaultAsync
,则为FALSE。修改了答案以修复该错误,以及另一个可能的错误(不会传播异常)。你的替补队员很好。
public class ConnectionStatus
{
private Subject<Unit> pending = new Subject<Unit>();
private Subject<Unit> connected = new Subject<Unit>();
public bool IsConnected { get; private set; }
public ConnectionStatus(CancellationToken token, short timeoutSeconds = 15)
{
pending
.Select(outer =>
Observable.Amb(
connected.Select(_ => true),
Observable.Timer(TimeSpan.FromSeconds(timeoutSeconds)).Select(_ => false)))
.Switch()
.Subscribe(isConnected => IsConnected = isConnected, token);
}
public void ConfirmConnected()
{
connected.OnNext(Unit.Default);
}
public void SetPending()
{
pending.OnNext(Unit.Default);
}
}