1. 程式人生 > >《C#併發程式設計經典例項》—— 用限流和抽樣抑制事件流

《C#併發程式設計經典例項》—— 用限流和抽樣抑制事件流

問題

有時事件來得太快,這是編寫響應式程式碼時經常碰到的問題。一個速度太快的事件流可導

致程式的處理過程崩潰。

解決方案

Rx 專門提供了幾個操作符,用來對付大量湧現的事件資料。Throttle 和 Sample 這兩個操 作符提供了兩種不同方法來抑制快速湧來的輸入事件。

Throttle 建立了一個超時視窗,超時期限可以設定。當一個事件到達時,它就重新開始計 時。當超時期限到達時,它就把視窗內到達的最後一個事件釋出出去。

下面的例子也是監視滑鼠移動,但使用了 Throttle,在滑鼠保持靜止 1 秒後才報告最近一 條移動事件。

private void Button_Click(object sender, RoutedEventArgs e)

{

Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(

handler =&gt; (s, a) =&gt; handler(s, a), handler =&gt; MouseMove += handler, handler =&gt; MouseMove -= handler)

.Select(x =&gt; x.EventArgs.GetPosition(this))

.Throttle(TimeSpan.FromSeconds(1))

.Subscribe(x =&gt; 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&gt;(

handler => (s, a) =&gt; 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 根據事件 的資料過濾。在抑制快速湧來的輸入流時,這三種操作符提供了三種不同的方法