我有一个程序,我正在接收事件并希望分批处理它们,以便在我处理当前批次时进入的所有项目都将出现在下一批中。
Rx中简单的TimeSpan和基于计数的缓冲区方法将为我提供多批项目,而不是给我一大堆已经进入的项目(如果订阅者花费的时间超过指定的TimeSpan或超过N项进来,N大于数。)
我查看了使用Func<IObservable<TBufferClosing>>或IObservable<TBufferOpening> and Func<TBufferOpening, IObservable<TBufferClosing>>的更复杂的Buffer重载,但我找不到如何使用这些的示例,更不用说弄清楚如何将它们应用于我我想做。
答案 0 :(得分:1)
这样做你想要的吗?
var xs = new Subject<int>();
var ys = new Subject<Unit>();
var zss =
xs.Buffer(ys);
zss
.ObserveOn(Scheduler.Default)
.Subscribe(zs =>
{
Thread.Sleep(1000);
Console.WriteLine(String.Join("-", zs));
ys.OnNext(Unit.Default);
});
ys.OnNext(Unit.Default);
xs.OnNext(1);
Thread.Sleep(200);
xs.OnNext(2);
Thread.Sleep(600);
xs.OnNext(3);
Thread.Sleep(400);
xs.OnNext(4);
Thread.Sleep(300);
xs.OnNext(5);
Thread.Sleep(900);
xs.OnNext(6);
Thread.Sleep(100);
xs.OnNext(7);
Thread.Sleep(1000);
我的结果:
1-2-3
4-5
6-7
答案 1 :(得分:1)
你需要什么来缓冲价值,然后是工人 准备好它要求当前缓冲区,然后重置它。这个可以 完成RX和任务
的组合class TicTac<Stuff> {
private TaskCompletionSource<List<Stuff>> Items = new TaskCompletionSource<List<Stuff>>();
List<Stuff> in = new List<Stuff>();
public void push(Stuff stuff){
lock(this){
if(in == null){
in = new List<Stuff>();
Items.SetResult(in);
}
in.Add(stuff);
}
}
private void reset(){
lock(this){
Items = new TaskCompletionSource<List<Stuff>>();
in = null;
}
}
public async Task<List<Stuff>> Items(){
List<Stuff> list = await Items.Task;
reset();
return list;
}
}
然后
var tictac = new TicTac<double>();
IObservable<double> source = ....
source.Subscribe(x=>tictac.Push(x));
然后在你的工作人员
while(true){
var items = await tictac.Items();
Thread.Sleep(100);
for each (item in items){
Console.WriteLine(item);
}
}
答案 2 :(得分:1)
我之前采用的方法是在DotPeek / Reflector中提取ObserveOn方法并采用它所具有的排队概念并使其适应我们的要求。例如,在具有快速滴答数据(如财务)的UI应用程序中,UI线程可能充斥着事件,有时它无法更快地更新。在这些情况下,我们希望删除除最后一个之外的所有事件(对于特定的工具)。在这种情况下,我们将ObserveOn的内部队列更改为单个值T(查找ObserveLatestOn(IScheduler))。在您的情况下,您需要队列,但是您想要推送整个队列而不仅仅是第一个值。这应该可以帮到你。
答案 3 :(得分:0)
@Enigmativity答案的扩展。我用它来解决问题:
public static IObservable<(Action ready, IReadOnlyList<T> values)> BufferUntilReady<T>(this IObservable<T> stream)
{
var gate = new BehaviorSubject<Guid>(Guid.NewGuid());
void Ready() => gate.OnNext(Guid.NewGuid());
return stream.Publish(shared => shared
.Buffer(gate.CombineLatest(shared, ValueTuple.Create)
.DistinctUntilChanged(new AnyEqualityComparer<Guid, T>()))
.Where(x => x.Any())
.Select(x => ((Action) Ready, (IReadOnlyList<T>) x)));
}
public class AnyEqualityComparer<T1, T2> : IEqualityComparer<(T1 a, T2 b)>
{
public bool Equals((T1 a, T2 b) x, (T1 a, T2 b) y) => Equals(x.a, y.a) || Equals(x.b, y.b);
public int GetHashCode((T1 a, T2 b) obj) => throw new NotSupportedException();
}
当准备接收下一个缓冲区时,订阅者会收到一个Ready()函数,该函数将被调用。我不会在同一线程上观察每个缓冲区来避免循环,但是如果您需要在同一线程上处理每个缓冲区,我想您可以将它打乱。