《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 的重載 能創建互相重合的組,還可跳過組之間的元素。還有一些重載可使用委托,可對組的邊界 進行動態定義。
最後更新:2017-05-23 11:02:37