1. 程式人生 > >《C#併發程式設計經典例項》—— 用視窗和緩衝對事件分組

《C#併發程式設計經典例項》—— 用視窗和緩衝對事件分組

問題

有一系列事件,需要在它們到達時進行分組。舉個例子,需要對一些成對的輸入作出響

應。第二個例子,需要在 2 秒鐘的視窗期內,對所有輸入進行響應。

解決方案

Rx 提 供 了 兩 個 對 到 達 的 序 列 進 行 分 組 的 操 作:Buffer 和 Window。Buffer 會 留 住 到 達 的 事 件, 直 到 收 完 一 組 事 件, 然 後 會 把 這 一 組 事 件 以 一 個 集 合 的 形 式 一 次 性 地 轉 送 過 去。 Window 會在邏輯上對到達的事件進行分組,但會在每個事件到達時立即傳遞過去。Buffer 的返回型別是 IObservable<IList<T>(由若干個集合組成的事件流);Window 的返回型別 是 IObservable<IObservable<T>(由若干個事件流組成的事件流)。

下面的例子使用 Interval,每秒建立 1 個 OnNext 通知,然後每 2 個通知做一次緩衝:

private void Button_Click(object sender, RoutedEventArgs e)

{

Observable.Interval(TimeSpan.FromSeconds(1))

.Buffer(2)

.Subscribe(x => Trace.WriteLine(

DateTime.Now.Second + ": Got " + x[0] + " and " + x[1]));

}

用我的電腦測試,每 2 秒產生 2 個輸出:

13: Got 0 and 1

15: Got 2 and 3

17: Got 4 and 5

19: Got 6 and 7

21: Got 8 and 9

下面的例子有些類似,使用 Window 建立一些事件組,每組包含 2 個事件:



private void Button_Click(object sender, RoutedEventArgs e)

{

Observable.Interval(TimeSpan.FromSeconds(1))

.Window(2)

.Subscribe(group =>

{

});

}

Trace.WriteLine(DateTime.Now.Second + ": Starting new group");

group.Subscribe(

x => Trace.WriteLine(DateTime.Now.Second + ": Saw " + x),

() => Trace.WriteLine(DateTime.Now.Second + ": Ending group"));

用我的電腦測試,輸出的結果就是這樣:

17: Starting new group

18: Saw 0

19: Saw 1

19: Ending group

19: Starting new group

20: Saw 2

21: Saw 3

21: Ending group

21: Starting new group

22: Saw 4

23: Saw 5

23: Ending group

23: Starting new group

這幾個例子說明了 Buffer 和 Window 的區別。Buffer 等待組內的所有事件,然後把所有事 件作為一個集合釋出。Window 用同樣的方法進行分組,但它是在每個事件到達時就釋出。

Buffer 和 Window 都可以使用時間段作為引數。在下面的例子中,所有的滑鼠移動事件被 收集進視窗,每秒一個視窗:

private void Button_Click(object sender, RoutedEventArgs e)

{

Observable.FromEventPattern<;MouseEventHandler, MouseEventArgs>;(

handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler => MouseMove -= handler)

.Buffer(TimeSpan.FromSeconds(1))

.Subscribe(x => Trace.WriteLine(

DateTime.Now.Second + ": Saw " + x.Count + " items."));

}

輸出的結果依賴於怎麼移動滑鼠,類似於這樣:

49: Saw 93 items.

50: Saw 98 items.

51: Saw 39 items.

52: Saw 0 items.

53: Saw 4 items.

54: Saw 0 items.

55: Saw 58 items.

討論

Buffer 和 Window 可用來抑制輸入資訊,並把輸入塑造成我們想要的樣子。另一個實用技 術是限流(throttling),將在 5.4 節介紹。

Buffer 和 Windows 都有其他過載,可用在更高階的場合。引數為 skip 和 timeShift 的過載 能建立互相重合的組,還可跳過組之間的元素。還有一些過載可使用委託,可對組的邊界 進行動態定義。