2011-12-24 77 views
4

我要尋找一個合適的模式和解決以下問題最現代的方式:處理在C#.NET4應用程序在後臺多個輸入

我的應用程序期待來自多個來源的輸入,例如:圖形用戶界面,監控文件系統,語音命令,網絡請求等。當接收到輸入時,我需要將其發送到某些ProcessInput(InputData arg)方法,該方法將開始在後臺處理數據,而不會阻止應用程序接收和處理更多數據,而在某些只要處理完成,方法就會返回一些結果。根據輸入,處理可能需要大量不同的時間。對於初學者,我不需要檢查進度或取消處理的能力。

閱讀對MSDN和一些搖滾明星程序員,我真搞此處應該使用什麼樣的模式,更重要的是它的.NET

我的發現功能的文章的相關博客文章一打後:

  1. ThreadPool.QueueUserWorkItem - 最簡單的理解,關於將結果返回
  2. BackgroundWorker不是十分便利 - 似乎只能針對比較簡單的任務,所有工人在單個線程上運行使用?
  3. Event-based Asynchronous Pattern
  4. Tasks in Task Parallel Library
  5. C# 5 async/await - 這似乎是從工作任務的快捷方式並行

注:

性能是很重要的,所以服用多核系統的優勢時,可能會真的很好。

這不是一個web應用程序。

我的問題讓我想起了一個TCP服務器(真的是任何類型的服務器),其中應用程序不斷監聽多個套接字上的新連接/數據,我發現文章Asynchronous Server Socket,我很好奇這個模式是否可能成爲可能的解決方案爲了我。

回答

3

我的應用程序期待來自多個來源的輸入,例如:GUI,監視文件系統,語音命令,網頁請求等

我已經做在一大堆異步編程的我的時間。我發現區分後臺操作異步事件是有用的。 「後臺操作」是您啓動的事情,並在一段時間後完成。 「異步事件」是獨立於您的程序而始終進行的事情;您可以訂閱,接收一段時間的事件,然後取消訂閱。

因此,GUI輸入和文件系統監控將是異步事件的例子;而Web請求是後臺操作。後臺操作也可以分成CPU綁定(例如,處理流水線中的一些輸入)和I/O綁定(例如web請求)。

我在.NET中特別強調這個區別,因爲不同的方法有不同的優缺點。在進行評估時,您還需要考慮如何傳播錯誤

首先,你已經找到的選項:

  1. ThreadPool.QueueUserWorkItem - 幾乎晝夜最壞的選擇。它只能處理後臺操作(無事件),並且不能很好地處理I/O綁定操作。返回結果和錯誤都是手動的。
  2. BackgroundWorker(BGW) - 不是最差的,但絕對不是最好的。它也只處理後臺操作(無事件),並且不能很好地處理I/O綁定操作。每個BGW都運行在自己的線程中 - 這很糟糕,因爲您無法利用線程池的工作竊取自我平衡特性。此外,完成通知(通常)都排隊等待一個線程,這可能會導致非常繁忙的系統出現瓶頸。
  3. 基於事件的異步模式(EAP) - 這是您的列表中的第一個支持異步事件以及後臺操作的選項,它還可以高效地處理I/O綁定操作。然而,正確編程有點困難,並且它具有與完成通知(通常)全部排隊到單個線程的BGW相同的問題。 (請注意,BGW是應用於CPU綁定後臺操作的EAP)。我寫了a library來幫助編寫EAP組件,以及一些基於EAP的套接字。但我不推薦這種方法;現在有更好的選擇。
  4. Tasks在任務並行庫中 - Task是CPU限制和I/O限制的後臺操作的最佳選擇。 I review several background operation options on my blog - 但該博客文章根本不涉及異步事件。
  5. C#5 async/await - 這些允許更自然地表達基於Task的後臺操作。如果您想要(對UI初始化操作有用),它們還提供了一種簡單的方法來同步回調用方的上下文。

在這些選項中,async/await是最容易使用的,與Task緊隨其後。這些問題在於它們被設計用於後臺操作而不是異步事件。

只要您有足夠的緩衝區來處理這些事件,則可以使用異步操作(例如,Task)來使用任何異步事件源。當你有一個緩衝區時,你可以在每次完成時重新啓動異步操作。一些緩衝區由操作系統提供(例如,套接字具有讀取緩衝區,UI窗口具有消息隊列等),但您可能必須自己提供其他緩衝區。

話雖如此,這是我的建議:

  1. Task-based Asynchronous Pattern (TAP) - 無論是使用await/asyncTask直接用自來水至少模型的後臺操作。
  2. TPL DataflowVS Async的一部分) - 允許您爲數據傳輸設置「管線」。數據流基於Task。 Dataflow的缺點是它仍在開發中,並且(IMO)不如其他Async支持那麼穩定。
  3. Reactive Extensions (Rx) - 這是專門爲異步事件設計的唯一選項,而不僅僅是後臺操作。它正式發佈(與VS Async和Dataflow不同),但學習曲線更陡峭。

所有這三個選項都是有效的(對任何實際處理使用線程池),並且它們都具有定義良好的錯誤處理和結果的語義。我建議儘可能使用TAP;那些部件可以很容易地集成到Dataflow或Rx中。

您提到了「語音命令」作爲一個可能的輸入源。您可能對Stephen Toub唱歌的BuildWindows video感興趣,並使用數據流近乎實時地協調他的聲音。 (Stephen Toub是TPL,Dataflow和Async背後的天才之一)。

+0

BuildWindows視頻通常很有教育意義。謝謝! – m0s 2012-01-08 07:30:08

2

IMO使用線程池是WRT處理輸入的方式。看看http://smartthreadpool.codeplex.com。它提供了一個非常好的API(使用泛型)來等待結果。您可以將此與異步服務器套接字實現結合使用。看看Jeff Richter的Power Threading Lib也許值得一看:http://www.wintellect.com/Resources/Downloads

+0

感謝您的指點,我正在檢查智能線程池,看起來有趣且複雜......這類任務總是很複雜:/ – m0s 2011-12-24 12:03:37

1

我絕不是這方面的專家,但是我最近在這方面做了一些研究,我對結果非常滿意與MS TPL庫。任務爲ThreadPool線程提供了一個很好的包裝,並且針對並行處理進行了優化,以確保更高的性能。如果你能夠爲你的項目使用.NET 4.0,你應該探索使用任務。它們代表了處理異步操作的更高級的方式,並提供了一種使用CancellationToken對象取消正在進行的操作的好方法。

下面是從不同的使用線程任務訪問UI線程的小例子:

private void TaskUse() 
    { 
     var task = new Task<string>(() => 
      { 
       Thread.Sleep(5000); 
       return "5 seconds passed!"; 
      }); 
     task.ContinueWith((tResult) => 
      { 
       TestTextBox.Text = tResult.Result; 
      }, TaskScheduler.FromCurrentSynchronizationContext()); 
     task.Start(); 
    } 

從前面的例子中可以看到是多麼容易與UI線程使用TaskScheduler.FromCurrentSynchronizationContext()同步,假設你從UI線程調用此方法。任務還提供阻塞操作的優化,例如您需要等待服務響應的場景,以及通過在Task構造函數中提供TaskCreationOptions.LongRunning枚舉值。這將確保指定的操作不會阻塞處理器內核,因爲活動任務的最大數量由當前處理器內核的數量決定。

相關問題