C# 在可观察序列中除去最后一个样本,直到消费者准备好为止
仪器观测有一个实时序列C# 在可观察序列中除去最后一个样本,直到消费者准备好为止,c#,system.reactive,reactive-programming,C#,System.reactive,Reactive Programming,仪器观测有一个实时序列IObservable。还有一个async消费函数f:S->Task(具有副作用),处理值可能需要时间。不得重新输入该函数。然而,一旦它返回,就应该将流中的下一个可用样本传递给它。此外,函数必须看到流末尾的最后一个示例 感觉这一定是一种非常常见的模式,但我找不到惯用的解决方案。我的方法是有效的,但在反馈循环中,使用主题作为命令信号变量的做法有点不雅观。我的问题有惯用的解决办法吗 static void Main() { var source = Observable.I
IObservable
。还有一个async
消费函数f:S->Task
(具有副作用),处理值可能需要时间。不得重新输入该函数。然而,一旦它返回,就应该将流中的下一个可用样本传递给它。此外,函数必须看到流末尾的最后一个示例
感觉这一定是一种非常常见的模式,但我找不到惯用的解决方案。我的方法是有效的,但在反馈循环中,使用主题作为命令信号变量的做法有点不雅观。我的问题有惯用的解决办法吗
static void Main() {
var source = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(27);
var s = source.Publish(ps => {
var signal = new Subject<Unit>();
return ps
.SkipLast(1)
.Window(signal)
.SelectMany(w => w.Take(1))
.Merge(ps.PublishLast().RefCount())
.Select(x => Observable.FromAsync(() => LengthyWork(x)))
.Concat()
.Do(signal.OnNext);
});
var d = s.Subscribe();
Thread.Sleep(5000);
Console.WriteLine("Stopping");
d.Dispose();
}
static async Task<Unit> LengthyWork(long n) {
Console.WriteLine($"Processing {n}");
await Task.Delay(800);
return Unit.Default;
}
你可以使用最新的运算符-我想你需要的是一个简单的答案:我找不到一个简单的解决方案,因为你也希望发布最后一个值,即使它不在时间范围内
// signal to indicate we want more events to flow
var signal = new BehaviorSubject<bool>(true);
var source = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(27).Publish();
// Observable to the source but have it skip the last value (would have published the same value twice if not doing this. Ex: if take was 33 instead of 27)
var sequence = source.SkipLast(1).Publish();
// Observable that is just the lastvalue in the sequence
var last = source.PublishLast();
var d = signal.DistinctUntilChanged()
.Where(on => on) // only let go if we have signal set to true
.SelectMany(last.Do(_ => signal.OnCompleted()).Merge(sequence).Take(1)) // Side effect of last is to turn off signal
.Subscribe(
async ps =>
{
signal.OnNext(false); // no more values from the source
await LengthyWork(ps);
signal.OnNext(true); // resubscribe to the source
});
// wire it all up
last.Connect();
sequence.Connect();
source.Connect();
Thread.Sleep(5000);
Console.WriteLine("Stopping");
d.Dispose();
//表示我们希望更多事件流动的信号
var信号=新行为主体(真);
var source=Observable.Interval(TimeSpan.frommilluses(100)).Take(27.Publish();
//源可以观察到,但让它跳过最后一个值(如果不这样做,将发布相同的值两次。例如:如果take是33而不是27)
var sequence=source.SkipLast(1.Publish();
//可观察的,这只是序列中的最后一个值
var last=source.PublishLast();
var d=signal.DistinctUntilChanged()
.Where(on=>on)//仅当信号设置为true时才释放
.SelectMany(last.Do(=>signal.OnCompleted()).Merge(sequence.Take(1))//last的副作用是关闭信号
.订阅(
异步ps=>
{
signal.OnNext(false);//源中不再有值
等待长度工作(ps);
signal.OnNext(true);//重新订阅源
});
//把一切都接好
last.Connect();
sequence.Connect();
source.Connect();
睡眠(5000);
控制台。写入线(“停止”);
d、 处置();
部分想法来自MSDN论坛 您说“该函数不能重新输入。但是,一旦返回,流中的下一个可用样本就应该传递给它。”-默认情况下Rx就是这样工作的。@Enigmativity:确实是,但这是一个
异步
函数。当函数产生时,线程从它返回(因此从Observer.OnNext
),比如当执行命中它内部的wait
时。在我的情况下,我需要保证整个异步FSA完成,否则我将冒着发送多条网络消息的风险,这些消息竞相发送并且到达时出现故障。@Enigmativity:如果运行示例,请删除.Window(signal)。选择many(w=>w.Take(1))
部分以查看发生的情况。我尝试,但这看起来和我的窗口解决方案一样好,因为它是一个推挽转换器,可能会重复这个元素。它的朋友最近阻止线程以避免这种情况,这不是最好的方法(两者都返回IEnumerable)。另外,我想不出一种方法来满足在两种解决方案中观察最后一个结束前元素的要求。谢谢,我想我很赞同这个想法。我目前的工作解决方案涉及相当低级别的实现,包括锁定,需要完整的多线程推理,因此需要复杂的测试;简言之,我不喜欢它。我要看看你的解决方案能帮我走多远。
// signal to indicate we want more events to flow
var signal = new BehaviorSubject<bool>(true);
var source = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(27).Publish();
// Observable to the source but have it skip the last value (would have published the same value twice if not doing this. Ex: if take was 33 instead of 27)
var sequence = source.SkipLast(1).Publish();
// Observable that is just the lastvalue in the sequence
var last = source.PublishLast();
var d = signal.DistinctUntilChanged()
.Where(on => on) // only let go if we have signal set to true
.SelectMany(last.Do(_ => signal.OnCompleted()).Merge(sequence).Take(1)) // Side effect of last is to turn off signal
.Subscribe(
async ps =>
{
signal.OnNext(false); // no more values from the source
await LengthyWork(ps);
signal.OnNext(true); // resubscribe to the source
});
// wire it all up
last.Connect();
sequence.Connect();
source.Connect();
Thread.Sleep(5000);
Console.WriteLine("Stopping");
d.Dispose();