我有一个热点可观测量,它以不同的数字随机发出。
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
答案 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
如果这不是您正在寻找的内容,请澄清问题。