通常,并行处理仅与CPU密集型操作相关。但是,PLINQ使用WithDegreeOfParallelism扩展专门提供IO密集型支持。例如:
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操作?那么“延迟”效应怎么样?
例如,如果通过WCF服务调用的IO抛出CommunicationException,我可能希望在转到下一个资源之前使用“3次尝试”策略再次发出相同的请求。我可能还想在每次尝试之间等待一分钟。虽然我在每次尝试之间“等待”一分钟,但我不希望线程被阻塞等待。
在此MSDN article中,作者从类似于我上面显示的查询开始。然后他转换查询,以便没有线程阻塞。不幸的是,他在这个过程中失去了WithDegreeOfParallelism。无论哪种方式,他都没有解决发生错误时“重试”的问题。
任何人都有或知道这样做的光滑方式?
我正在考虑制作一个特殊的IEnumerable包装器,允许在PLINQ走动集合时“重新插入”值。这确实会导致“重试”行为,但仍然不允许“重试之间的1分钟延迟”要求。我可以使用Thread.Sleep()创建1分钟的延迟,但我试图不阻止线程。
思想?
答案 0 :(得分:0)
您linked的文章实际上显示了如何使用基于Task
的API(DownloadDataTask
)来避免阻止IO绑定操作的线程:
然而,这段代码还有一些不太理想的东西。 工作(发送下载请求和阻止)几乎需要 没有CPU,但它是由ThreadPool线程完成的,因为我正在使用它 默认调度程序。理想情况下,线程只应用于CPU绑定 工作(当有实际工作时)。
使用PLINK / Task.Run
/ Task.Factory.StartNew
进行基于IO的操作是反模式。 PLINQ(与Parallel.For
等相同)适用于CPU绑定计算工作,但是为自然异步网络/ IO绑定操作分配和阻塞线程没有意义,它不需要线程一直在“飞行中”。要按照您显示的示例代码进行操作,就像new Ping().SendAsync(site)
一样,返回Task
。然后,您可以执行await Task.WhenAll(tasks)
并处理错误。
请参阅Stephen Cleary的"There Is No Thread",以及他最近的answer解决并行IO的最大程度。最重要的是,很容易合并重试逻辑,而不涉及任何线程(例如,像this)。