我有一些使用異步編程模型(APM)習慣用語(例如BeginRead
/EndRead
)在C#中編寫的高性能文件傳輸代碼。該代碼從本地磁盤讀取文件並將其寫入套接字。使用任務並行庫執行異步I/O的建議
爲了在現代硬件上獲得最佳性能,儘可能在飛行中保留多個未完成的I/O操作非常重要。因此,我在該文件上發佈了幾個BeginRead
操作,然後當完成時,我在套接字上調用BeginSend
,完成後我在文件上執行另一個BeginRead
。細節比這個更復雜一些,但是在這個想法的高層次上。
我已經有了基於APM的代碼,但它很難遵循,並且可能有微妙的併發錯誤。我很樂意使用TPL代替它。我想Task.Factory.FromAsync
只是要做,但有一個問題。
我見過的所有I/O示例(最特別是並行擴展附件中的StreamExtensions
類)假定一次讀取後再寫入一次。這不會以我需要的方式執行。
我不能用一些簡單的像Parallel.ForEach
或其它功能擴展Task.Factory.Iterate
因爲異步I/O任務不會花很多時間在一個工作線程,因此並行剛剛啓動另一個任務,從而可能導致數十或數百待處理的I/O操作;太多了!你可以在你的任務上解決這個問題,但是這會導致創建一個事件句柄(一個內核對象),並阻塞等待一個任務等待句柄,該句柄綁定一個工作線程。我的基於APM的實現避免了這兩件事。
我一直在玩各種不同的方式來保持多個讀/寫操作在飛行中,並且我設法使用延續來調用創建另一個任務的方法,但它感覺尷尬,而且肯定不會感覺不像慣用的TPL。
有沒有其他人正在用TPL解決這個問題?有什麼建議麼?
感謝您的回覆。這是真的,我可以做到這一點,但'Parallel.ForEach'方式在運行任務中實現了'Wait'。我曾希望避免這種情況,因爲: 'Wait'使用'ManualResetEventSlim',它嘗試一個自旋鎖,然後恢復爲一個內核的Event對象。 創建內核事件對象需要很多指令並切換到內核模式。 等待內核事件對象需要切換到內核模式。 由於我不需要使用APM實現這些功能,所以我希望有一些習慣性的TPL方式可以避免它們。 – anelson 2010-05-13 15:09:53
執行I/O時,最後需要擔心的是「許多指令和切換到內核模式」。但是由於'EndRead'也在等待事件句柄,所以我不理解你的擔心。 – Gabe 2010-05-13 15:59:31
我在自己的測試工具中嘗試了這一點,我很驚訝,做這些等待的懲罰是多麼微不足道。 這不是我想要的答案,但它似乎是正確的,所以我接受了這個答案。 – anelson 2010-05-13 20:19:23