2010-03-30 61 views
36

什麼時候會選擇使用Rx而不是TPL還是兩個框架是正交的?TPL vs反應框架

根據我的理解,Rx主要是爲了提供對事件的抽象並允許組合,但它也允許提供對異步操作的抽象。 使用Createxx重載和Fromxxx重載並通過處置返回的IDisposable來取消。

TPL還通過任務和取消功能提供操作的抽象。

我的困境是什麼時候使用哪種方法和什麼情況?

+0

鏈接到舊的,關閉,重複的問題:http://stackoverflow.com/questions/2138361/reactive-framework-vs-plinq-vs-task-parallel-library-vs-parallel-extensions/2188259 – yzorg 2013-01-21 18:12:21

回答

39

Rx的主要目的不是提供對事件的抽象。這只是其結果之一。它的主要目的是爲集合提供可組合的推式模型。

反應性框架(Rx)基於IObservable<T>IEnumerable<T>的數學對偶。因此,我們可以通過IObservable<T>將對象「推送」給我們,而不是使用IEnumerable<T>從集合中「拉」物品。

當然,當我們真的去尋找可觀察的來源時,事件&異步操作是非常好的選擇。

反應性框架自然需要一個多線程模型才能夠觀察可觀察數據的來源並管理查詢和訂閱。 Rx實際上大量使用TPL來做到這一點。

因此,如果您使用Rx,則隱式使用TPL。

如果您希望直接控制任務,您可以直接使用TPL。

但是,如果您有希望觀察和執行查詢的數據源,則我會徹底推薦使用響應式框架。

+2

我會比如說,如果你不考慮線程問題,'提供對事件的抽象'這個短語相當於'事件對象集合(又名DTO對象)的推模式'。 – yzorg 2013-01-21 18:45:23

+3

非常好的答案,在實際應用中基本屬實。然而,Rx可以在沒有TPL的情況下使用。另外,Rx可以用在完全單線程的應用程序中,而不需要任何併發(更像是WPF或NodeJS如何在單個線程中實現異步)。我真的很喜歡你的電梯廣告預測的最後一句話,我會說「它的主要目的是爲數據序列提供一個可組合的推式模型。」我發現越早開發人員按順序思考而不是集合,他們越快Rk。 – 2014-07-17 14:41:25

23

一些準則,我喜歡遵循:

  • 我我處理,我不發起數據。數據何時到達?然後RX。
  • 我的計算是否需要管理併發?然後TPL。
  • 我是否管理多個結果,並且需要根據時間選擇它們?然後RX。
11

我喜歡Scott W的要點。要多放些具體的例子在 的Rx地圖真的很好,以

  • 消費流
  • 進行非阻塞異步工作就像Web請求。
  • 流式事件(或者。如鼠標移動或服務總線的消息類型的事件淨事件)
  • 作曲事件的「流」起來
  • LINQ的風格操作
  • 從公共API揭露的數據流

TPL似乎很好地映射到

  • 工作的內部並行化
  • 執行非阻塞異步工作像Web請求
  • 執行工作流程和延續

有一件事我已經注意到用的IObservable(Rx)的是,它的應用日益普及。一旦進入你的代碼庫,它無疑會通過其他接口暴露出來,它最終會出現在你的應用程序中。我想這可能起初很可怕,但現在大部分團隊都對Rx很滿意,並且喜歡它爲我們節省的工作量。

恕我直言,Rx將成爲TPL上的主流圖書館,因爲它已經在.NET 3.5,4.0,Silverlight 3,Silverlight 4和Javascript中受到支持。這意味着您必須有效地學習一種風格,並且適用於很多平臺。

編輯:我已經改變了我對Rx在TPL上占主導地位的想法。他們解決不同的問題,所以不應該這樣比較。使用.NET 4.5/C#5.0,async/await關鍵字將進一步將我們與TPL(這是很好的)相結合。有關的Rx深discuson VS事件VS TPL等檢查了我的在線圖書的first chapterIntroToRx.com

9

更新,2016年12月:如果你有30分鐘,我建議你讀喬·達菲的第一手帳戶,而不是我的猜測。我認爲我的分析很好,但如果你發現這個問題,我強烈建議你看看博客帖子,而不是這些答案,因爲除了TPL和Rx.NET,他還包括MS研究項目(Midori,Cosmos)。

http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/


我認爲微軟取得了修正後的.NET 2.0出來了一個很大的錯誤。他們從公司的不同部分同時引入了許多不同的併發管理API。

  • 史蒂芬Toub在竭力推動線程安全的原語來替代事件(其開始爲Future<T>,變成Task<T>
  • MS研究了MIN-LINQ和無擴展器(Rx)
  • 硬件/嵌入式有robotics cuntime (CCR)

在此期間許多管理API團隊試圖用APM和Threadpool.QueueUserWorkItem(),不知道生活,如果Toub會贏得他的鬥爭出貨Future<T>/mscorlib.dll中的。最後,它看起來像被對衝了,並且在mscorlib中同時發送了Task<T>IObservable<T>,但在mscorlib中不允許任何其他Rx API(甚至不包括ISubject<T>)。我認爲這個對衝最終導致了大量的重複(更晚),並浪費了公司內外的努力。

對於複製見:TaskIObservable<Unit>Task<T>AsyncSubject<T>Task.Run()Observable.Start()。這只是冰山一角。但是,在更高層次上考慮:

  • 的StreamInsight - SQL事件流,本地代碼優化,但使用LINQ語法
  • TPL數據流定義的事件查詢 - 建立在第三方物流,建立平行的Rx,優化調整線程平行度,不擅長撰寫查詢
  • Rx - 驚人的表現力,但充滿危險。將'熱門'流與IEnumerable風格的擴展方法混合在一起,這意味着您可以非常輕鬆地永遠阻止(在熱點流上調用First()永不返回)。調度限制(限制並行)是通過相當奇怪的SubscribeOn()擴展方法完成的,這些擴展方法很古怪,很難得到正確的結果。如果開始學習Rx預留很長時間來學習所有避免的陷阱。但是,如果組合複雜的事件流或者需要複雜的過濾/查詢,Rx纔是真正的唯一選擇。

我不認爲Rx有廣泛採用戰鬥的機會,直到MS船舶ISubject<T>在mscorlib。這很令人傷心,因爲Rx包含一些非常有用的具體(通用)類型,如TimeInterval<T>Timestamped<T>,我認爲它應該位於Core/mscorlib中,如Nullable<T>。另外,System.Reactive.EventPattern<TEventArgs>

+1

搞笑拼寫錯誤。 – 2014-08-27 02:16:01

+1

@DaveSexton IObservalbe? – yzorg 2014-08-28 18:25:15

+1

我認爲@DaveSexton意味着SteamInsight。但是,在一個嚴肅的說明中,感謝您對這些框架進行了最有力的分析!這個國際海事組織應該是被接受的答案! – kkm 2016-04-27 06:08:11

0

我做了一個Windows Phone應用程序。從RX開始。問題是,在某個時候,我使用了一個新版本的RX,並猜測是什麼?許多功能都有「過時」屬性。那麼我開始使用TPL,現在我有兩個混合項目。我的建議是,如果你使用很多異步網絡電話,那麼最好使用TPL。正如已經寫過的,RX正在使用TPL,爲什麼不從一開始就使用TPL。

+0

Rx的一個主要優點是異步代碼變得更容易編寫和遵循。使用TPL你最終會處理嵌套的回調,使得代碼難以遵循和調試。 Rx團隊會爲標記爲「過時」的人提出「等效」呼叫,你是否已經向MSDN提及過時的呼叫以找到他們的新等價物? – 2013-09-28 16:34:38

+0

我會將我的應用程序重寫爲通用應用程序,並使用引用PRISM應用程序AdventureWorksShopper作爲基準。我在這個應用程序中看到的是架構非常好,所有RX功能都可以在TPL中使用,因此不再需要RX。我不認爲TPL更難讀,那麼RX對我來說學習曲線是一樣的。我想要的是一個未來的證明框架。我不喜歡過時。過時意味着:我們沒有錢來支持它了。無論如何,這就是我的想法。 – 2014-08-24 12:31:03

4

我會說TPL Dataflow涵蓋了Rx中功能的特殊子集。數據流用於可能需要可測量的時間的數據處理,而Rx用於處理時間可忽略不計的事件,如鼠標位置,錯誤狀態等。

例如:您的「訂閱」處理程序是異步的,並且您希望當時不超過1個執行程序。使用Rx必須阻止,因爲Rx是異步不可知的,並且不會在許多地方以特殊方式威脅異步。

.Subscribe(myAsyncHandler().Result) 

如果你沒有阻塞,那麼當處理程序仍然異步執行時,Rx會認爲該動作已完成。

你可能會想,如果你做

.ObserveOn(Scheduler.EventLoopSchedule) 

不是問題就解決了。但是這會破壞你的.Complete()工作流程,因爲Rx會在它計劃執行後立即認爲它已經完成,並且你將退出應用程序而不等待異步操作完成。

如果您希望允許不超過4個併發異步任務,那麼Rx不會提供任何開箱即用的任務。也許你可以通過實現你自己的調度程序,緩衝區等來破解一些東西。

TPL Dataflow在ActionBlock中提供了非常好的解決方案。它可以將同時執行的操作壓縮到某個數字,並且它理解異步操作,因此調用Complete()並等待Completed將完成您期望的操作:等待所有正在進行的異步任務完成。

TPL的另一個特點是「backpressure」。假設您在處理例程時發現了一個錯誤,需要重新計算上個月的數據。如果您使用Rx訂閱源代碼,並且您的管道包含無限制緩衝區或ObserveOn,那麼在數秒內就會耗盡內存,因爲源代碼的讀取速度比處理能夠處理的快。即使你實現阻止消費者,你的源可能會阻塞呼叫,例如,如果源是異步的。在TPL中,您可以實現源爲

while(...) 
    await actionBlock.SendAsync(msg) 

它不會阻止源,但會在處理程序重載時等待。總的來說,我發現Rx非常適合時間和計算輕的動作。如果處理時間變得很長,你就會處於奇怪的副作用和深奧調試的世界。

好消息是,TPL Dataflow塊與Rx玩的很好。它們具有AsObserver/AsObservable適配器,您可以在需要時將它們粘貼到Rx管道的中間。但是Rx有更多的模式和用例。所以我的經驗法則是從Rx開始,並根據需要添加TPL Dataflow。