用于错误处理的并行I / O和重试逻辑

时间:2014-03-07 00:12:09

标签: c# multithreading error-handling io plinq

通常,并行处理仅与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分钟的延迟,但我试图不阻止线程。

思想?

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)。