《C#併發程式設計經典例項》—— 用限流和抽樣抑制事件流
問題
有時事件來得太快,這是編寫響應式程式碼時經常碰到的問題。一個速度太快的事件流可導
致程式的處理過程崩潰。
解決方案
Rx 專門提供了幾個操作符,用來對付大量湧現的事件資料。Throttle 和 Sample 這兩個操 作符提供了兩種不同方法來抑制快速湧來的輸入事件。
Throttle 建立了一個超時視窗,超時期限可以設定。當一個事件到達時,它就重新開始計 時。當超時期限到達時,它就把視窗內到達的最後一個事件釋出出去。
下面的例子也是監視滑鼠移動,但使用了 Throttle,在滑鼠保持靜止 1 秒後才報告最近一 條移動事件。
private void Button_Click(object sender, RoutedEventArgs e) { Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>( handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler => MouseMove -= handler) .Select(x => x.EventArgs.GetPosition(this)) .Throttle(TimeSpan.FromSeconds(1)) .Subscribe(x => Trace.WriteLine( DateTime.Now.Second + ": Saw " + (x.X + x.Y))); }
輸出結果依賴於滑鼠的實際動作,我的測試結果是這樣:
47: Saw 139
49: Saw 137
51: Saw 424
56: Saw 226
Throttle 常用於類似“文字框自動填充”這樣的場合,使用者在文字框中輸入文字,當他停 止輸入時,才需要進行真正的檢索。為抑制快速運動的事件序列,Sample 操作符使用了另一種方法。Sample 建立了一個有規律 的超時時間段,每個時間段結束時,它就釋出該時間段內最後的一條資料。如果這個時間 段沒有資料,就不釋出。
下面的例子捕獲滑鼠移動,每隔一秒取樣一次。 與 Throttle 不同,使用 Sample 的例子中, 不需要讓滑鼠靜止一段時間,就可要看到結果。
private void Button_Click(object sender, RoutedEventArgs e) { Observable.FromEventPattern>MouseEventHandler, MouseEventArgs>( handler => (s, a) => handler(s, a), handler => MouseMove += handler, handler =>MouseMove -= handler) .Select(x => x.EventArgs.GetPosition(this)) .Sample(TimeSpan.FromSeconds(1)) .Subscribe(x => Trace.WriteLine( DateTime.Now.Second + ": Saw " + (x.X + x.Y))); }
我先讓滑鼠靜止幾秒鐘,然後連續移動,得到了下面的輸出結果:
12: Saw 311
17: Saw 254
18: Saw 269
19: Saw 342
20: Saw 224
21: Saw 277
討論
對於快速湧來的輸入,限流和抽樣是很重要的兩種工具。別忘了還有一個過濾輸入的簡單方 法, 就 是 採 用 標 準 LINQ 的 Where 操 作 符。 可 以 這 樣 說,Throttle 和 Sample 操 作 符 與
Where 基本差不多,唯一的區別是 Throttle、Sample 根據時間段過濾,而 Where 根據事件 的資料過濾。在抑制快速湧來的輸入流時,這三種操作符提供了三種不同的方法