C# 反应条件节流算子
我正在寻找一个类似的运算符,但节流行为仅在布尔值为true时应用。因此,给定两个可观察的C# 反应条件节流算子,c#,.net,reactive-programming,system.reactive,C#,.net,Reactive Programming,System.reactive,我正在寻找一个类似的运算符,但节流行为仅在布尔值为true时应用。因此,给定两个可观察的IObservable值和IObservable throttleCondition,我想创建一个可观察的,它执行以下操作: 将通过值中的所有值,直到 当throttleCondition生成true时。然后,直到 当throttleCondition再次生成false时。然后传播序列的最后一个值,并再次传递任何新值 或者,用大理石图表示(注意4): 我得到的最接近的结果是: public static
IObservable值
和IObservable throttleCondition
,我想创建一个可观察的,它执行以下操作:
- 将通过
值中的所有值,直到
- 当
生成throttleCondition
时。然后,直到true
- 当
再次生成throttleCondition
时。然后传播序列的最后一个值,并再次传递任何新值false
public static IObservable<T> ThrottleWhen<T>(this IObservable<T> self, IObservable<bool> throttleCondition)
{
bool cond = false;
throttleCondition.Subscribe(v => cond = v);
return self.Window(throttleCondition).Select(obs => cond ? obs.TakeLast(1) : obs).SelectMany(obs => obs);
}
public static IObservable ThrottleWhen(此IObservable self,IObservable throttleCondition)
{
bool cond=假;
throttleCondition.Subscribe(v=>cond=v);
返回self.Window(throttleCondition).Select(obs=>cond?obs.TakeLast(1):obs.SelectMany(obs=>obs);
}
但这不是线程安全的,因为Select和Subscribe之间可能存在竞争。
有人有什么建议吗?也许已经有一个接线员了,但我没有看到
编辑:
下面是我需要的函数的单元测试:
[TestMethod]
public void TestThrottleWhen()
{
//Setup
var scheduler = new TestScheduler();
Subject<int> numberValues = new Subject<int>();
Subject<bool> flagValues = new Subject<bool>();
//Define actions
scheduler.Schedule(TimeSpan.FromTicks(1), () => flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(10), () => numberValues.OnNext(0));
scheduler.Schedule(TimeSpan.FromTicks(20), () => numberValues.OnNext(1));
scheduler.Schedule(TimeSpan.FromTicks(30), () => flagValues.OnNext(true));
scheduler.Schedule(TimeSpan.FromTicks(40), () => numberValues.OnNext(2));
scheduler.Schedule(TimeSpan.FromTicks(50), () => numberValues.OnNext(3));
scheduler.Schedule(TimeSpan.FromTicks(60), () => numberValues.OnNext(4));
scheduler.Schedule(TimeSpan.FromTicks(70), () => flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(71), () => flagValues.OnNext(true));
scheduler.Schedule(TimeSpan.FromTicks(72), () => flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(80), () => numberValues.OnNext(5));
scheduler.Schedule(TimeSpan.FromTicks(90), () => numberValues.OnNext(6));
var actual = scheduler.Start(() => numberValues.ThrottleWhen(flagValues), 0, 0, 100);
//Assert
var expected = new[]
{
ReactiveTest.OnNext(10, 0),
ReactiveTest.OnNext(20, 1),
ReactiveTest.OnNext(70, 4),
ReactiveTest.OnNext(80, 5),
ReactiveTest.OnNext(90, 6)
};
ReactiveAssert.AreElementsEqual(expected, actual.Messages);
}
[TestMethod]
公共void TestThrottleWhen()
{
//设置
var scheduler=newtestscheduler();
主题编号值=新主题();
Subject flagValues=新主题();
//定义动作
scheduler.Schedule(TimeSpan.FromTicks(1),()=>flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(10),()=>numberValues.OnNext(0));
scheduler.Schedule(TimeSpan.FromTicks(20),()=>numberValues.OnNext(1));
scheduler.Schedule(TimeSpan.FromTicks(30),()=>flagValues.OnNext(true));
scheduler.Schedule(TimeSpan.FromTicks(40),()=>numberValues.OnNext(2));
scheduler.Schedule(TimeSpan.FromTicks(50),()=>numberValues.OnNext(3));
scheduler.Schedule(TimeSpan.FromTicks(60),()=>numberValues.OnNext(4));
scheduler.Schedule(TimeSpan.FromTicks(70),()=>flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(71),()=>flagValues.OnNext(true));
scheduler.Schedule(TimeSpan.FromTicks(72),()=>flagValues.OnNext(false));
scheduler.Schedule(TimeSpan.FromTicks(80),()=>numberValues.OnNext(5));
scheduler.Schedule(TimeSpan.FromTicks(90),()=>numberValues.OnNext(6));
var-actual=scheduler.Start(()=>numberValues.ThrottleWhen(flagValues),0,0,100);
//断言
预期风险值=新[]
{
反应性测试.OnNext(10,0),
反应性测试.OnNext(20,1),
反应性测试.OnNext(70,4),
反应性测试.OnNext(80,5),
反应性测试.OnNext(90,6)
};
ReactiveAssert.arElementSequal(预期的、实际的.Messages);
}
编辑2:
我最终使用了Alex答案的修改版本:
public static IObservable<T> ThrottleWhen<T>(this IObservable<T> self, IObservable<bool> throttleCondition)
{
var isPaused = throttleCondition.Prepend(false).DistinctUntilChanged();
return Observable.Defer(() =>
{
object lockObj = new object();
bool gateIsOpen = false;
return Observable.CombineLatest(
self.Synchronize(lockObj).Do(_ => gateIsOpen = true),
isPaused.Synchronize(lockObj).Do(paused => gateIsOpen = !paused && gateIsOpen),
(number, paused) => (number, paused)
)
.Where(tuple => !tuple.paused && gateIsOpen)
.Select(tuple => tuple.number);
});
}
public static IObservable ThrottleWhen(此IObservable self,IObservable throttleCondition)
{
var isPaused=throttleCondition.Prepend(false).DistinctUntilChanged();
返回可观察的延迟(()=>
{
object lockObj=新对象();
bool-gateIsOpen=false;
返回可观测的(
self.Synchronize(lockObj).Do(=>gateIsOpen=true),
isPaused.Synchronize(lockObj.Do)(暂停=>gateIsOpen=!暂停(&gateIsOpen)),
(数字,暂停)=>(数字,暂停)
)
.Where(tuple=>!tuple.paused&&gateIsOpen)
.Select(tuple=>tuple.number);
});
}
我希望这将是最后一次尝试^^
回到CombineTest方法,结合您的解决方案,我提出了一个额外的标志,它将帮助我们避免出现这样的情况,即标志来回变化,而可观察的值不会发出任何值
bool numberChangedFlag = false;
_numberValues
.Do(_ => numberChangedFlag = true)
.CombineLatest(_flagValues.Do(x => numberChangedFlag = !x && numberChangedFlag), (number, flag) => (number, flag))
.Where(tuple => !tuple.flag && numberChangedFlag)
.Select(tuple => tuple.number)
.Subscribe(DoYourMagic);
我在你的测试中试过了,看起来效果不错
虽然我不喜欢我们需要一个局部helper变量来解决这个问题,但深入一点并创建一个helper observable似乎会使代码过于复杂
让我知道这次是否有效。:) 我希望这将是最后一次尝试^^ 回到CombineTest方法,结合您的解决方案,我提出了一个额外的标志,它将帮助我们避免出现这样的情况,即标志来回变化,而可观察的值不会发出任何值
bool numberChangedFlag = false;
_numberValues
.Do(_ => numberChangedFlag = true)
.CombineLatest(_flagValues.Do(x => numberChangedFlag = !x && numberChangedFlag), (number, flag) => (number, flag))
.Where(tuple => !tuple.flag && numberChangedFlag)
.Select(tuple => tuple.number)
.Subscribe(DoYourMagic);
我在你的测试中试过了,看起来效果不错
虽然我不喜欢我们需要一个局部helper变量来解决这个问题,但深入一点并创建一个helper observable似乎会使代码过于复杂
让我知道这次是否有效。:) 我认为这更干净,但干净在旁观者眼里: 在核心部分,解决方案如下所示:
public static IObservable<T> ThrottleWhenCore<T>(this IObservable<T> self, IObservable<bool> throttleCondition)
{
return throttleCondition
.StartWith(false)
.Join(self,
b => b ? Observable.Empty<bool>() : throttleCondition,
t => self,
(b, t) => (b, t)
)
.Where(tuple => !tuple.b)
.Select(tuple => tuple.t);
}
如果你需要更多的解释,让我知道。与锁定/状态变量相比,我更喜欢它。我认为这更干净,但“干净”是旁观者眼中的: 在核心部分,解决方案如下所示:
public static IObservable<T> ThrottleWhenCore<T>(this IObservable<T> self, IObservable<bool> throttleCondition)
{
return throttleCondition
.StartWith(false)
.Join(self,
b => b ? Observable.Empty<bool>() : throttleCondition,
t => self,
(b, t) => (b, t)
)
.Where(tuple => !tuple.b)
.Select(tuple => tuple.t);
}
如果你需要更多的解释,让我知道。与锁定/状态变量相比,我更喜欢这种方式。这里还有另一种可能性,基于@Enigmativity的评论。考虑这一点的最佳方式是将其分为两个问题:当条件变为真时获取最新信息,当条件变为假时自由发射,然后将两者合并在一起
public static IObservable<T> ThrottleWhen<T>(this IObservable<T> self, IObservable<bool> throttleCondition)
{
return throttleCondition.Publish(_throttleCondition => self.Publish(_self => Observable.Merge(
_throttleCondition //Get latest when true handler
.Where(b => !b)
.WithLatestFrom(
_self.Select((item, index) => (item, index)),
(_, t) => t
)
.DistinctUntilChanged()
.Select(t => t.item),
_throttleCondition //Freely emit when false, default start with false.
.StartWith(false)
.Select(b => b ? Observable.Empty<T>(): _self)
.Switch()
)));
}
public static IObservable ThrottleWhen(此IObservable self,IObservable throttleCondition)
{
返回throttleCondition.Publish(\u throttleCondition=>self.Publish(\u self=>observeable.Merge(
_throttleCondition//Get latest when true处理程序
.其中(b=>!b)
.最晚从(
_self.Select((项目,索引)=>(项目,索引)),
(u,t)=>t
)