如何使用Rx.NET?

时间:2016-07-02 03:19:26

标签: c# system.reactive

我有一个可观察的序列IObservable<int>,我希望将其转换为IObservable<IList<int>>,同时保留以下要求:

  • 最终的观察结果是一系列批次,但有两种 - A B A 批次包含1000每个项目,其中 B 批次每个包含400个项目。
  • 原始序列中的每个数字必须分批两次 - 一次在 A 批次中,另一次在某些 B 批次中
  • 处理应该是动态的,并且两种批次应该并行生产。即首先生成所有 A 批次然后所有 B 批次都不可接受的解决方案。

我可以使用Buffer运算符轻松生成一种批次,但我不知道如何在同一数据上生成两批。

修改

这是一个简单的代码,只生成一种批处理。

IObservable<int> source = GetSource(...);
await source
    .Buffer(1000)
    .Select(batch => Observable.FromAsync(() => ProcessBatchAsync(batch)))
    .Merge(MaxConcurrentBatches)
    .DefaultIfEmpty();
...

private async Task<Unit> ProcessBatchAsync(IList<int> batch)
{
   ...
   return Unit.Default;
}

我想要的是:

  • 两个项目的Observable,其中每个项目是另一个只有一种批次的可观察项目。当批量可观察量为。
  • 时,主要的可观察性应该是完整的
  • 单个Observable,可生成两种批次。然后我需要根据monad的种类切换不同的运算符。

EDIT2

我需要详细说明约束。原始observable位于SqlReader对象的顶部,订阅它两次意味着读者创建两次并且数据库访问加倍。我只需要一个订阅。

EDIT3

对于示例数据,我们可以使用Observable.Range(0,10000)。鉴于该序列,我需要按任何顺序进行以下批处理:

[0..1000), [0..400), [1000..2000),[400..800),[2000..3000),[800..1200),[3000..4000),[1200..1600) ... [9000..10000) ... [9600..10000)

或者您可以使用范围为[0..100]的批次为10和4的数字。这并不重要,因为解决方案不应取决于批量大小,也不取决于批次类型的数量。

例如,它应适用于3批10,4和6号。或任何其他组合。

EDIT4

我认为我把人与我的约束混为一谈。当我说“交错”时,我并不是说批次类型必须严格旋转。这正是我试图解释必须同时生产批次的不同类型。给定3个批次类型A,B和C,有可能偶尔一个接一个地生产两批A型。然而,如果首先是A类的所有批次,然后是B类的所有批次,然后是所有C类批次,那是不可接受的。

3 个答案:

答案 0 :(得分:2)

我认为这会产生你需要的东西:

var query =
    Observable
        .Range(0, 10000)
        .Publish(ns =>
            ns
                .Buffer(1000)
                .Concat(Observable.Repeat(new List<int>() as IList<int>))
                .Zip(ns.Buffer(400), (n1s, n2s) => new [] { n1s, n2s })
                .SelectMany(nns => nns)
                .Where(xs => xs.Any()));

根据您的示例输出正确交错。

如果我将数字减少100倍,那么我得到这个输出:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9 
0, 1, 2, 3 
10, 11, 12, 13, 14, 15, 16, 17, 18, 19 
4, 5, 6, 7 
20, 21, 22, 23, 24, 25, 26, 27, 28, 29 
8, 9, 10, 11 
30, 31, 32, 33, 34, 35, 36, 37, 38, 39 
12, 13, 14, 15 
40, 41, 42, 43, 44, 45, 46, 47, 48, 49 
16, 17, 18, 19 
50, 51, 52, 53, 54, 55, 56, 57, 58, 59 
20, 21, 22, 23 
60, 61, 62, 63, 64, 65, 66, 67, 68, 69 
24, 25, 26, 27 
70, 71, 72, 73, 74, 75, 76, 77, 78, 79 
28, 29, 30, 31 
80, 81, 82, 83, 84, 85, 86, 87, 88, 89 
32, 33, 34, 35 
90, 91, 92, 93, 94, 95, 96, 97, 98, 99 
36, 37, 38, 39 
40, 41, 42, 43 
44, 45, 46, 47 
48, 49, 50, 51 
52, 53, 54, 55 
56, 57, 58, 59 
60, 61, 62, 63 
64, 65, 66, 67 
68, 69, 70, 71 
72, 73, 74, 75 
76, 77, 78, 79 
80, 81, 82, 83 
84, 85, 86, 87 
88, 89, 90, 91 
92, 93, 94, 95 
96, 97, 98, 99 

如果你不需要它们严格交错,那么这是一种推广n缓冲区的方法:

var buffers = new [] { 1000, 400, 500, 300 };
var source = Observable.Range(0, 10000);
var result = source.Publish(ss => buffers.Select(b => ss.Buffer(b)).Merge());

答案 1 :(得分:0)

以下方法如何

var observable = Observable.Range(0, 10000);

Task.Run(() => observable.Buffer(400).Subscribe(buffer => /* process buffer */ ));
Task.Run(() => observable.Buffer(1000).Subscribe(buffer => /* process buffer */ ));

将同时生产两种批次。

答案 2 :(得分:0)

此代码创建两个IObservable<IList<int>>序列。

var source = Observable.Range(0,10000)
                       .Publish();

var batchesOf1000 = source.Buffer(1000);
var batchesOf400 = source.Buffer(400);

batchesOf1000.Subscribe(batch => batch.Dump());
batchesOf400.Subscribe(batch => batch.Dump());

var disposable = source.Connect();