簡體   English   中英

如何使用 Rx.NET 為相同的數據生成多個不同大小的批次?

[英]How to produce several batches of different size for the same data using Rx.NET?

我有一個可觀察序列IObservable<int>並且我想將它轉換為IObservable<IList<int>>同時保留以下要求:

  • 最后的 observable 是一系列批次,但有兩種類型 - ABA批次每個包含 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 完成時,主 observable 應該是完整的。
  • 產生兩種批次的單個 Observable。 然后我需要根據 monad 的類型切換不同的操作符。

編輯2

我需要詳細說明約束。 原始 observable 位於SqlReader對象之上,訂閱它兩次意味着讀取器被創建兩次並且數據庫訪問被加倍。 我只需要一個訂閱。

編輯3

對於示例數據,我們可以使用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 個數字的批次一起使用。 這並不重要,因為解決方案不應依賴於批次大小或批次類型的數量。

例如,應適用於具有 10、4 和 6 個號碼的 3 個批次。 或任何其他組合。

編輯4

我想我讓人們對我的限制感到困惑。 當我說“交錯”時,並不是說批次類型必須嚴格輪換。 這正是我試圖解釋不同類型的批次必須同時生產的方式。 給定 3 個批次類型 A、B 和 C,可能偶爾會接連生產兩批 A 類型。 然而,如果首先是所有類型 A 的批次,然后是所有類型 B 的批次,然后是所有類型 C 的批次,這是不可接受的。

我認為這會產生你需要的東西:

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());

正如 Theo 指出的,第一個查詢中使用的Observable.Repeat有可能產生大量的對象。 如果使用Observable.Range這不會發生,但如果Observable.Interval是那么這是一個大問題。

我很懶惰,根本沒有嘗試進行數學計算來限制所需的填充物數量。

很容易修復。

var total_item_count = 100;
var batch_a_size = 10;
var batch_b_size = 4;

var filler =
    Observable
        .Repeat(
            new List<long>() as IList<long>,
            Math.Abs(total_item_count / batch_a_size - total_item_count / batch_b_size) + 1);

var query =
    Observable
        .Interval(TimeSpan.FromSeconds(0.01))
        .Take(total_item_count)
        .Publish(ns =>
            ns
                .Buffer(batch_a_size)
                .Concat(filler)
                .Zip(
                    ns
                        .Buffer(batch_b_size)
                        .Concat(filler),
                    (n1s, n2s) => new [] { n1s, n2s })
                .SelectMany(nns => nns)
                .Where(xs => xs.Any()));

我得到與以前相同的輸出,現在它可以為每個批次大小進行配置,並且沒有逃跑的Repeat

下面的方法怎么樣

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

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

兩種批次將並行生產。

此代碼創建兩個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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM