Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 转发带有冷却的接收项目,当它们来的太快时切换到采样_.net_System.reactive_Throttling - Fatal编程技术网

.net 转发带有冷却的接收项目,当它们来的太快时切换到采样

.net 转发带有冷却的接收项目,当它们来的太快时切换到采样,.net,system.reactive,throttling,.net,System.reactive,Throttling,我正在寻找Rx方法,该方法将采取可观察的方式,并将最新的物品置于“冷却”状态,这样当物品进入速度慢于冷却时间时,它们只会被转发,但当它们进入速度快时,您只会在每个冷却期后获得最新的值 public static IObservable<T> LimitRate<T>( this IObservable<T> source, TimeSpan duration, IScheduler scheduler) { re

我正在寻找Rx方法,该方法将采取可观察的方式,并将最新的物品置于“冷却”状态,这样当物品进入速度慢于冷却时间时,它们只会被转发,但当它们进入速度快时,您只会在每个冷却期后获得最新的值

    public static IObservable<T> LimitRate<T>(
        this IObservable<T> source, TimeSpan duration, IScheduler scheduler)
    {
        return source.DistinctUntilChanged()
                     .GroupByUntil(k => 0,
                                   g => Observable.Timer(duration, scheduler))
            .SelectMany(x => x.FirstAsync()
                              .Merge(x.Skip(1)
                                      .TakeLast(1)))
                              .Select(x => Observable.Return(x)
                                .Concat(Observable.Empty<T>()
                                    .Delay(duration, scheduler)))
                                    .Concat();
    }
换一种方式说,当项目之间的间隔小于
t
时间时,我想切换到周期
t
采样(当项目分散时切换回)

这与实际操作非常相似,只是每当新项目到达时,计时器不会重置

我想到的应用程序是通过网络发送“最新价值”更新。我不想传达一个值,除非它已经改变,我也不想对一个快速变化的值发送垃圾邮件,以至于我淹没了其他数据


是否有一种标准方法可以满足我的需要?

您可以使用
Observable.DistinctUntilChanged
Observable.Sample

可观察到。明显改变

仅当曲面值与以前的值不同时,此方法才会显示曲面值。()

可观察到。样本

Sample方法只是在每个指定的时间跨度内获取最后一个值。()


为了产生所需的效果,您可以将生成的第一个项目与上面描述的项目组合起来。

Strilanc,考虑到您担心源流安静时会出现不必要的活动,您可能会对这种调整事件节奏的方法感兴趣-否则我不打算添加它,我认为J.Lennon的实现是完全合理的(而且简单得多),而且计时器的性能不会受到影响

在这个实现中还有一个有趣的区别——它不同于
Sample
方法,因为它会立即发出在冷却期之外发生的事件,而不是在下一个采样间隔。它在冷却时间之外不保持计时器

编辑-这里是v3解决了Chris在评论中提到的问题-它确保了在冷却过程中发生的变化会触发新的冷却期

    public static IObservable<T> LimitRate<T>(
        this IObservable<T> source, TimeSpan duration, IScheduler scheduler)
    {
        return source.DistinctUntilChanged()
                     .GroupByUntil(k => 0,
                                   g => Observable.Timer(duration, scheduler))
            .SelectMany(x => x.FirstAsync()
                              .Merge(x.Skip(1)
                                      .TakeLast(1)))
                              .Select(x => Observable.Return(x)
                                .Concat(Observable.Empty<T>()
                                    .Delay(duration, scheduler)))
                                    .Concat();
    }
自我回答

虽然我问的是Rx,但我的实际情况是它的一个端口(ReactiveCocoa)。更多的人知道Rx,我可以翻译

无论如何,我最终直接实现了它,以便它能够满足我想要的延迟/性能属性:

-(RACSignal*)cooldown:(NSTimeInterval)cooldownPeriod onScheduler:(RACScheduler *)scheduler {
    need(cooldownPeriod >= 0);
    need(!isnan(cooldownPeriod));
    need(scheduler != nil);
    need(scheduler != RACScheduler.immediateScheduler);

    force(cooldownPeriod != 0); //todo: bother with no-cooldown case?
    force(!isinf(cooldownPeriod)); //todo: bother with infinite case?

    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        need(subscriber != nil);

        NSObject* lock = [NSObject new];
        __block bool isCoolingDown = false;
        __block bool hasDelayedValue = false;
        __block id delayedValue = nil;
        __block RACDisposable *cooldownDisposer = nil;
        void (^onCanSendValue)(void) = ^{
            @synchronized (lock) {
                // check that we were actually cooling down
                // (e.g. what if the system thrashed before we could dispose the running-down timer, causing a redundant call?)
                if (!isCoolingDown) {
                    return;
                }

                // if no values arrived during the cooldown, we do nothing and can stop the timer for now
                if (!hasDelayedValue) {
                    isCoolingDown = false;
                    [cooldownDisposer dispose];
                    return;
                }

                // forward latest value
                id valueToSend = delayedValue;
                hasDelayedValue = false;
                delayedValue = nil;
                // todo: can this be avoided?
                // holding a lock while triggering arbitrary actions cam introduce subtle deadlock cases...
                [subscriber sendNext:valueToSend];
            }
        };
        void (^preemptivelyEndCooldown)(void) = ^{
            // forward latest value AND ALSO force cooldown to run out (disposing timer)
            onCanSendValue();
            onCanSendValue();
        };

        RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
            bool didStartCooldown;
            @synchronized (lock) {
                hasDelayedValue = true;
                delayedValue = x;
                didStartCooldown = !isCoolingDown;
                isCoolingDown = true;
            }

            if (didStartCooldown) {
                // first item gets sent right away
                onCanSendValue();
                // coming items have to wait for the timer to run down
                cooldownDisposer = [[RACSignal interval:cooldownPeriod onScheduler:scheduler]
                                    subscribeNext:^(id _) { onCanSendValue(); }];
            }
        } error:^(NSError *error) {
            preemptivelyEndCooldown();
            [subscriber sendError:error];
        } completed:^{
            preemptivelyEndCooldown();
            [subscriber sendCompleted];
        }];

        return [RACDisposable disposableWithBlock:^{
            [selfDisposable dispose];
            @synchronized (lock) {
                isCoolingDown = false;
                [cooldownDisposer dispose];
            }
        }];
    }] setNameWithFormat:@"[%@ cooldown:%@]", self.name, @(cooldownPeriod)];
}
-(RACSignal*)冷却:(NSTIMEIVAL)调度程序上的冷却周期:(RACScheduler*)调度程序{
需要(冷却期>=0);
需要(!isnan(冷却期));
需要(调度程序!=nil);
需要(调度器!=RACScheduler.immediateScheduler);
force(冷却周期!=0);//todo:不考虑冷却案例?
force(!isinf(冷却期));//todo:麻烦处理无限大的情况吗?
return[[RACSignal createSignal:^(id订户){
需要(订户!=无);
NSObject*lock=[NSObject new];
__块bool isCoolingDown=false;
__块bool hasdayedvalue=false;
__块id delayedValue=nil;
__块RAC*冷却处理器=无;
作废(^onCanSendValue)(作废)=^{
@已同步(锁定){
//检查我们是否真的在冷却
//(例如,如果在我们可以处理正在运行的计时器之前,系统崩溃,导致冗余呼叫,该怎么办?)
如果(!isCoolingDown){
返回;
}
//如果在冷却期间没有值到达,我们什么也不做,现在可以停止计时器
如果(!hasDelayedValue){
isCoolingDown=false;
[冷却处置器处置];
返回;
}
//远期最新价值
id valueToSend=延迟值;
hasDelayedValue=false;
延迟值=零;
//托多:这可以避免吗?
//在触发任意操作时持有锁会导致微妙的死锁情况。。。
[订户发送下一步:valueToSend];
}
};
无效(^优先权和冷却)(无效)=^{
//转发最新值,并强制冷却耗尽(处理计时器)
onCanSendValue();
onCanSendValue();
};
RACDisposable*selfDisposable=[self subscribeNext:^(id x){
布尔·迪德斯塔特·科尔敦;
@已同步(锁定){
hasDelayedValue=true;
延迟值=x;
DIDSTARTCOLDOWN=!正在冷却;
isCoolingDown=true;
}
如果(DIDSTARTCOLDOWN){
//第一个项目立即发送
onCanSendValue();
//即将到来的项目必须等待计时器运行
cooldownDisposer=[[RACSignal interval:cooldownPeriod onScheduler:scheduler]
subscribeNext:^(id){onCanSendValue();}];
}
}错误:^(N错误*错误){
抢占式冷却();
[订户发送错误:错误];
}已完成:^{
抢占式冷却();
[用户发送完成];
}];
返回[RAC一次性处置带块:^{
[自行处置];
@已同步(锁定){
isCoolingDown=false;
[冷却处置器处置];
}
}];
}]setNameWithFormat:@“[%@冷却时间:%@]”,self.name,@(冷却时间)];
}

它应该几乎直接转换为.Net RX。当物品停止到达时,它将停止执行任何工作,并将在尊重冷却时间的情况下尽快转发物品。

我意识到这一问题已经解决了一段时间,但我想提供另一种解决方案,我可以
public static IObservable<T> SampleImmediate<T>(this IObservable<T> source, TimeSpan dueTime)
{
    return source
        .GroupBy(x => 0)
        .SelectMany(group =>
        {
            return Observable.Create<T>(o =>
            {
                var connectable = group.Materialize().Publish();

                var sub = Observable.Merge(
                        connectable.Sample(dueTime),
                        connectable.Take(1)
                    )
                    .DistinctUntilChanged()
                    .Dematerialize()
                    .Subscribe(o);

                return new CompositeDisposable(connectable.Connect(), sub);
            });
        });
}
public static IObservable<T> Cooldown<T>(this IObservable<T> source, TimeSpan dueTime)
{
    return source
        .GroupByUntil(x => 0, group => group.Throttle(dueTime))
        .SelectMany(group => group.SampleImmediate(dueTime));
}