将具有不同发射间隔的可观察变换为另一个

时间:2018-03-05 20:02:09

标签: c# system.reactive

我有一个热点可观测量,它以不同的数字随机发出。

1--1 ----- 1--1-2--4

我正在寻找一种方法,在预定义的时间间隔内有重复项来获取此项并将其合并回序列,直到找到空间以绕过间隔阈值。 我已经实现了一个我认为不是最佳的解决方案,因为当我使用真实对象而不是整数在生产中测试它时,它会在系统中产生一种背压并且我看到CPU发疯了。以下是我到目前为止的测试。

using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Reactive.Testing;
using Xunit;
using Xunit.Abstractions;

namespace Specs{
    public  class CollectDuplicatesSpecs:ReactiveTest{
        private readonly ITestOutputHelper _outputHelper;

        public CollectDuplicatesSpecs(ITestOutputHelper outputHelper){
            _outputHelper = outputHelper;
        }

        [Fact]
        public void MethodName(){
            var testScheduler = new TestScheduler();
            var hotObservable = testScheduler.CreateHotObservable(OnNext(10, 1), OnNext(20, 1), OnNext(30, 1),OnNext(40, 1));
            var subject = new Subject<int>();
            hotObservable.Merge(subject).Window(TimeSpan.FromTicks(20), testScheduler).Select(observable => {
                observable.CollectDuplicates(i => i).Delay(TimeSpan.FromTicks(1), testScheduler).Subscribe(subject);
                return observable.Distinct();
            }).SelectMany(observable => observable).Subscribe(i => _outputHelper.WriteLine($"{testScheduler.Clock}-{i}"));

            testScheduler.AdvanceBy(160);
        }
    }

    public static class RxEx{
        public static IObservable<TSource> CollectDuplicates<TSource>(this IObservable<TSource> source, Func<TSource, int> keySelector = null) {
            return Observable.Create<TSource>(observer => {
                var dubplicateCollector = new DubplicateCollector<TSource>(keySelector);
                var duplicateCollectorSubscription = dubplicateCollector.Matches.Subscribe(observer);
                var disposable = source.Distinct(dubplicateCollector).Finally(dubplicateCollector.Dispose).Subscribe();
                return new CompositeDisposable(disposable, duplicateCollectorSubscription, dubplicateCollector);
            });
        }
    }

    public class DubplicateCollector<TSource> : IEqualityComparer<TSource>,IDisposable {
        private readonly Func<TSource, int> _keySelector;
        readonly Subject<TSource> _matches = new Subject<TSource>();

        public DubplicateCollector(Func<TSource, int> keySelector) {
            _keySelector = keySelector;
        }

        public IObservable<TSource> Matches => _matches;

        public bool Equals(TSource x, TSource y) {
            var equals = IsMatch(x, y);
            if (equals)
                _matches.OnNext(x);
            return equals;
        }

        private bool IsMatch(TSource x, TSource y) {
            if (_keySelector != null)
                return _keySelector(x).Equals(_keySelector(y));
            var equals = x != null && x.Equals(y);
            return equals;
        }

        public int GetHashCode(TSource obj) {
            return _keySelector(obj);
        }

        public void Dispose(){
            _matches?.Dispose();
        }
    }


}

打印

10-1
21-1
40-1
60-1

1 个答案:

答案 0 :(得分:0)

我努力想要得到你想要的东西:一些大理石图可能会有所帮助。我假设你基本上想要一个类似平滑算子的东西:如果消息突然出现,那么随着时间的推移将它们平滑掉。

根据this答案,您可以创建一个处理平滑的运算符:

public static class ObservableDrainExtensions
{
    public static IObservable<T> TimeDrained<T>(this IObservable<T> source, TimeSpan ts, IScheduler scheduler)
    {
        return source.Drain(x => Observable.Empty<T>().Delay(ts, scheduler).StartWith(x));
    }

    public static IObservable<T> TimeDrained<T>(this IObservable<T> source, TimeSpan ts)
    {
        return TimeDrained(source, ts, Scheduler.Default);
    }

    public static IObservable<TOut> Drain<TSource, TOut>(this IObservable<TSource> source,
        Func<TSource, IObservable<TOut>> selector)
    {
        return Observable.Defer(() =>
        {
            BehaviorSubject<Unit> queue = new BehaviorSubject<Unit>(new Unit());

            return source
                .Zip(queue, (v, q) => v)
                .SelectMany(v => selector(v)
                    .Do(_ => { }, () => queue.OnNext(new Unit()))
                );
        });
    }
}

Drain可以线性地平滑事物,TimeDrained基于TimeSpan这样做。您可以将其与GroupBy结合使用,为其添加不同的元素:

[Fact]
public void MethodName()
{
    var testScheduler = new TestScheduler();
    var hotObservable = testScheduler.CreateHotObservable(
        OnNext(10, 1), 
        OnNext(20, 1), 
        OnNext(30, 1), 
        OnNext(40, 1)
    );

    var ts = TimeSpan.FromTicks(20);

    hotObservable
        .GroupBy(i => i)            //comparison key
        .Select(g => g.TimeDrained(ts, testScheduler))
        .Merge()
        .Subscribe(i => Console.WriteLine($"{testScheduler.Clock}-{i}"));

    testScheduler.AdvanceBy(160);
}

输出是:

10-1
30-1
50-1
70-1

如果这不是您正在寻找的内容,请澄清问题。