.NET併發編程-反應式編程

本系列學習在.NET中的併發並行編程模式,實戰技巧

本小節開始學習反應式編程。本系列保證最少代碼呈現量,雖然talk is cheap, show me the code被奉爲圭臬,我的學習習慣是,只學習知識點,代碼不在當下立馬要用的時候不會認真去讀的,更何況在大多時候在手機閱讀更不順暢。

內容目錄

1、反應式編程2、.NET中的工具3、Reactive Extensions(Rx)反應式擴展通過nuget安裝Rx4、Rx主要用處GUI編程中合併事件Timer通知事件

1、反應式編程

就是將事件作爲數據流異步地監聽和處理的一種編程範式。類似編輯Excel表格時,某個單個元的值是由其他單元格值決定的公式,一旦其他單元格的值發生變化,此公式單個格也會隨之更新。

如果以聲明式描述操作,那麼就是函數式反應型編程。

反應式編程的好處就是通過描述一種簡單且可維護的方法來處理異步、無阻賽計算和IO,從而增加了對多核和多CPU硬件上計算資源的使用。事件的控制從“請求”變爲了“等待”。大數據實時流處理環境,都會使用到反應式編程,例如推文系統處理,日誌系統收集等。

2、.NET中的工具

.NET中是有基於委託的模型事件的。訂閱者的事件處理程序註冊一系列事件,並在調用時觸發事件。類似桌面應用程序中經常使用Button的Click事件,通過使用+=來註冊綁定事件。所以傳統意義上的事件就是來處理GUI用戶界面的交互。這種命令式編程難以處理多個複雜事件的編排,並且容易造成內存泄漏。

在F#中使用|>管道操作符可以連接多個事件。不熟悉F#,就不多做說明。主要看看C#如果處理。

3、Reactive Extensions(Rx)反應式擴展

LINQ 是 對 序 列 數 據 進 行 查 詢 的 一 系 列 語 言 功 能。 內 置 的 LINQ to Objects( 基 於 IEnumerable) 和 LINQ to Entities( 基 於 IQueryable) 是 兩 個 最 常 用 的 LINQ 提 供 者。另外還有很多提供者,並且大多數都採用相同的基本架構。查詢是延後執行的,只有在需要時纔會從序列中獲取數據。從概念上講,這是一種拉取模式。 在查詢過程中數據項是被逐個拉取出來的。

Reactive Extensions(Rx)把事件看作是依次到達的數據序列。因此,將 Rx 認作是 LINQ to events( 基 於 IObservable) 也 是 可 以 的, 它 與 其 他 LINQ 提 供 者 的 主 要 區 別 在 於, Rx 採用推送模式。就是說,Rx 的查詢規定了在事件到達時程序該如何響應。Rx 在 LINQ 的基礎上構建,增加了一些功能強大的操作符,作爲擴展方法。

通過nuget安裝Rx

Observable和Enumerable是對偶存在,很多方法相似可以相互轉換。像下面輸出結果都是1-5

// Observable對象
Observable.Range(15)
  .Subscribe(x => Console.WriteLine(x));

// Enumerable對象
foreach (var x in Enumerable.Range(15))
  Console.WriteLine(x);

Rx裏主要的接口有兩個,IObervable和 IObserver,上面Observable遍歷輸出只是簡化了IObserver對象的創建,Subscribe方法可以只傳入OnNext委託,也可以傳入完整的IObserver對象。

public interface IObservable<out T>
{
  IDisposable Subscribe(IObserver<T> observer);
}

public interface IObserver<in T>
{
  void OnCompleted();
  void OnError(Exception error);
  void OnNext(T value);
}
var observer = Observer.Create<int>(
  x => Console.WriteLine(x), // onNext參數(delegate)
  ex => { throw ex; }, // onError參數(delegate)
  () => { });  // onCompleted參數(delegate)

Observable.Range(15).Subscribe(observer);

4、Rx主要用處

Rx的有點主要在於能夠將傳統的異步編程方式從支離破碎的代碼調用中解放出來,將各個事件的處理連接起來放在一個單獨的方法中,增加代碼可讀和可維護。

GUI編程中合併事件

桌面編程中多個事件進行組合的情況,比如鼠標按下/移動/放開事件進行關聯處理,一般可能需要定一個變量Flag來標記狀態,管理比較混亂。Rx可以將它們合成一個事件。

var drag = from down in this.MouseDownAsObservable()
                       from move in this.MouseMoveAsObservable().TakeUntil(this.MouseUpAsObservable())
                       select new { move.X, move.Y };
// 利用擴展方法將Winform原有的事件變換爲 IObservable<T> 對象
    public static class FormExtensions
    {
        public static IObservable<MouseEventArgs> MouseMoveAsObservable(this Form form)
        
{
            return Observable.FromEventPattern<MouseEventArgs>(form, "MouseMove").Select(e => e.EventArgs);
        }

        public static IObservable<MouseEventArgs> MouseDownAsObservable(this Form form)
        
{
            return Observable.FromEventPattern<MouseEventArgs>(form, "MouseDown").Select(e => e.EventArgs);
        }

        public static IObservable<MouseEventArgs> MouseUpAsObservable(this Form form)
        
{
            return Observable.FromEventPattern<MouseEventArgs>(form, "MouseUp").Select(e => e.EventArgs);
        }
    }

Timer通知事件

在一定的時間間隔監視某個值的場景。下面例子中就是每隔5s檢查textbox值是否變化,變化了就更新label。比以往簡介了很多

// 每隔1秒監視一下watchTarget.Value的值
var polling =
  Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(5))
  .Select(_ => textBox1.Text)
  .DistinctUntilChanged(); // 只有在值發生變化時才引發事件(polling)
polling.Subscribe(msg => this.Invoke(new Action<string>((e) => this.label1.Text = e),msg));

先這樣草草結束吧,用到了再去詳細研究下。像Rx是以往沒聽過的概念,有些場景中確實好用,但習慣了以往的方式,也比較難嘗試,慢慢破圈慢慢生成吧。

今天報名增駕了三輪摩托車,準備過一把騎士的癮,客官老爺們有摩友的,可以交流交流

 

 

to be contiued!
下集:任務異步模型

 

寫給普通:

你以爲我沒回頭

我以爲你沒挽留

年輕的想法還是儘早安排落地

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章