我有一些GroupBy的测试代码可以正常工作......
代码
var sw = Stopwatch.StartNew();
int groupSize = 5;
var coreObservable = Observable
.Range(1, 20)
.Select((x, idx) => new { x, idx })
.GroupBy(x => x.idx / groupSize, x => x.x)
.Select(x => x.ToList())
.Replay()
.RefCount();
coreObservable.Subscribe(
x => x.Subscribe(y => Console.WriteLine("Event raised [Books: {0}, Timestamp: {1}]", string.Join("|", y), sw.Elapsed)),
() => Console.WriteLine("Subcription closed"));
coreObservable.Wait(); // blocking until observable completes
输出
Event raised [Values: 1|2|3|4|5, Timestamp: 00:00:00.3224002]
Event raised [Values: 6|7|8|9|10, Timestamp: 00:00:00.3268353]
Event raised [Values: 11|12|13|14|15, Timestamp: 00:00:00.3270101]
Event raised [Values: 16|17|18|19|20, Timestamp: 00:00:00.3270803]
Subcription closed
问题是当我尝试将Concat与此表达式一起使用时......
代码
var sw = Stopwatch.StartNew();
int groupSize = 5;
var coreObservable = Observable
.Range(1, 20)
.Select((x, idx) => new { x, idx })
.GroupBy(x => x.idx / groupSize, x => x.x)
.Select(x => x.ToList())
.Concat() // JUST ADDED THIS
.Replay()
.RefCount();
coreObservable.Subscribe(
x => Console.WriteLine("Event raised [Values: {0}, Timestamp: {1}]", string.Join("|", x), sw.Elapsed),
() => Console.WriteLine("Subcription closed"));
coreObservable.Wait(); // blocking until observable completes
输出
Event raised [Values: 1|2|3|4|5, Timestamp: 00:00:00.2728469]
Event raised [Values: , Timestamp: 00:00:00.2791311]
Event raised [Values: , Timestamp: 00:00:00.2793720]
Event raised [Values: , Timestamp: 00:00:00.2794617]
Subcription closed
注意只暴露了第一组值。
我使用GroupBy而不是Buffer的原因是因为我试图将其用作为突发数据源创建最大大小块的方法。原始的observable可能是项目数组,我想在单个事件中有太多项目时拆分数组。
我想使用Concat的原因是因为我希望能够在数组事件之间创建延迟,就像很多人推荐here一样。
答案 0 :(得分:2)
将Concat()
替换为Merge()
,它可以正常工作。
我相信你的问题的原因是,Concat()
在当前完成之前不会开始收听下一个序列。
Concat图表:
s1 --0--1--2-|
s2 -5--6--7--8--|
r --0--1--2--5--6--7--8--|
虽然Merge()
同时订阅所有子序列,但每当任何子项发布值时都会发布值。
合并图:
s1 --1--1--1--|
s2 ---2---2---2|
r --12-1-21--2|
因此,在您的情况下,Concat()
订阅IObservable<IList<int>>
中的第一个Select(x => x.ToList())
,发布值直到完成,然后订阅下一个序列。 GroupBy()
会为找到的每个组创建一个新的IGroupedObservable
流,但所有IGroupedObservable
将同时完成:当基础流完成时。
所以Concat()
监听第一个流直到它完成,但是当第一个流完成时,所有其他流也完成了(因为它们实际上都是相同的序列,只是按键分割),所以有没有为以下序列发布的值。
所有图表都是从here借来的,这是Rx的绝佳资源,我强烈建议您查看有关各种运算符如何工作的任何问题。
答案 1 :(得分:0)
你的问题可以减少到这样的事情,这可能更容易思考:
var sw = Stopwatch.StartNew();
var subject = new Subject<int>();
var o2 = subject.Where(i => i % 2 == 0).ToList();
var o3 = subject.Where(i => i % 3 == 0).ToList();
var o4 = subject.Where(i => i % 4 == 0).ToList();
var c = Observable.Concat(o2, o3, o4)
// .Replay()
// .RefCount()
//.Replay().RefCount() has no impact here.
;
c.Subscribe(
x => Console.WriteLine("Event raised [Values: {0}, Timestamp: {1}]", string.Join("|", x), sw.Elapsed),
() => Console.WriteLine("Subcription closed"));
for(int i = 0; i < 6; i++)
subject.OnNext(i);
subject.OnCompleted();
输出:
Event raised [Values: 0|2|4, Timestamp: 00:00:00.0002278]
Event raised [Values: , Timestamp: 00:00:00.0002850]
Event raised [Values: , Timestamp: 00:00:00.0003049]
Subcription closed
如果您使用大理石图表,它会是这样的:
s : 012345|
o2 : ------(024)|
o3 : ------(03) |
o4 : ------(04) |
cOut: ------(024)|
cSub: (So2)------(So3)(So4)
cSub shows when c subscribes to child observables. cOut shows c's output.
So2 means subscribe to o2, So3 means subscribe to o3, etc..
Concat
订阅传递给它的第一个observable,然后只在当前的observable完成时才订阅后续的observable。在我们的例子中,ToList
在源完成时转储整个列表时不会省略任何内容。因此o2
,o3
,o4
同时完成,但c
仅订阅o2
。在o2
完成后,它会尝试订阅其他人,但他们已经完成了。
至于如何修复它,Merge
会起作用,但我猜你想在第2组之前处理第1组,Merge
会破坏。