2014-03-07 43 views
1

通常,並行處理僅與CPU密集型操作相關。但是,PLINQ使用WithDegreeOfParallelism擴展特別提供IO密集型支持。例如:用於錯誤處理的並行I/O和重試邏輯

from site in new[] 
{ 
    "www.albahari.com", 
    "www.linqpad.net", 
    "www.oreilly.com", 
    "www.takeonit.com", 
    "stackoverflow.com", 
    "www.rebeccarey.com" 
} 
.AsParallel().WithDegreeOfParallelism(6) 
let p = new Ping().Send (site) 
select new 
{ 
    site, 
    Result = p.Status, 
    Time = p.RoundtripTime 
} 

但如果supporting IO is the goal of WithDegreeOfParallelism,怎能PLINQ進一步擴展或用於實現一個「重試」的效果,這是典型的IO操作?那麼「延遲」效應呢?

例如,如果IO通過WCF服務調用引發CommunicationException,我可能會希望再次使用「3次嘗試」策略進行相同的請求,然後再轉到下一個資源。我可能還需要在每次嘗試之間等待一分鐘。當我在每次嘗試之間「等待」一分鐘時,我不希望線程阻塞等待。

在這MSDN article作者開始與我上面顯示的類似的查詢。然後他轉換查詢,以便不阻塞任何線程。不幸的是,他在這個過程中失去了WithDegreeOfParallelism。無論如何,當發生錯誤時,他沒有解決「重試」問題。

任何人都知道或者知道如何做到這一點?

我正在考慮製作一個特殊的IEnumerable包裝器,允許在PLINQ正在收集集合時重新插入值。這確實會導致「重試」行爲,但它仍然不允許「重試之間1分鐘的延遲」要求。我可以使用Thread.Sleep()創建1分鐘的延遲,但我試圖不阻塞線程。

想法?

回答

0

linked實際上說明了如何避免使用Task基於API(DownloadDataTask)阻斷IO綁定操作線程,而不是文章:

然而,還是有一些關於這個代碼是不理想。 工作(發送下載請求和阻止)幾乎需要 沒有CPU,但由於我使用的是 默認調度程序,因此它由ThreadPool線程完成。理想情況下,線程只能用於CPU限制 工作(實際上有工作要做)。

使用PLINK/Task.Run/Task.Factory.StartNew用於基於IO的操作是一個反模式。 PLINQ(與Parallel.For相同)適合CPU限制的計算工作,但是爲自然異步網絡/ IO綁定操作分配和阻塞線程沒有任何意義,它根本不需要線程,而「in -飛行」。要按照您顯示的示例代碼進行操作,該代碼將與new Ping().SendAsync(site)類似,並返回Task。然後您可以執行await Task.WhenAll(tasks)並處理錯誤。

Stephen Cleary提及"There Is No Thread",他最近的answer解決了最大並行IO問題。最重要的是,合併重試邏輯非常簡單,不會涉及任何線程(例如,像this)。

+0

是的,文章的最後一個例子確實展示瞭如何擁有非阻塞線程,但是它忽略了WithDegreeOfParallism(),這意味着它可能會導致數百個IO調用,實際上我可能只需要5個未完成的IO請求。另外,IO的PLINQ可能是反模式,但它們通過提供WithDegreeOfParallelism()擴展來鼓勵反模式。我編輯了我的問題;請參閱我提供的鏈接,解釋WithDegreeOfParallelism是爲阻止IO而設計的。 –

+0

@Brent不要忘記PLINQ是在異步等待之前設計的。 – svick

+0

@BrentArias,這可能對客戶端應用程序可行,但對於服務器端應用程序+5每個請求更多的線程可能很容易殺死可伸縮性。你寫*「我可以使用Thread.Sleep()創建1分鐘的延遲,但是我試圖不阻塞線程。」*那麼爲什麼要用同步IO調用阻塞它們,而你可以使用'async/await' ?這並沒有太大的不同。 – Noseratio