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

C# 用于错误处理的并行I/O和重试逻辑,c#,multithreading,error-handling,io,plinq,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" } .AsParalle

通常,并行处理只与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
}
但如果是这样,PLINQ如何进一步扩展或用于实现“重试”效果,这是典型的IO操作?那么“延迟”效应呢

例如,如果IO通过WCF服务调用抛出一个CommunicationException,我可能希望在转到下一个资源之前使用“3次尝试”策略再次发出相同的请求。我可能还想在每次尝试之间等待一分钟。虽然每次尝试之间我都会“等待”一分钟,但我不希望线程在等待时被阻塞

在本文中,作者从一个类似于上面所示的查询开始。然后,他转换查询,以便没有线程阻塞。不幸的是,他在这个过程中失去了高度的平行性。不管怎样,他都没有提到错误发生时的“重试”问题

有人有或知道这样做的巧妙方法吗

我正在考虑制作一个特殊的IEnumerable包装器,允许在PLINQ遍历集合时“重新插入”值。这确实会导致“重试”行为,但仍然不允许“重试间隔1分钟”的要求。我可以使用Thread.Sleep()创建1分钟的延迟,但我正在尝试不阻止线程

想法?

这篇文章实际上展示了如何通过使用基于
任务的API(
DownloadDataTask
)来避免阻塞IO绑定操作的线程:

然而,这段代码仍然有一些不理想的地方。 这项工作(发送下载请求和阻止)几乎需要 没有CPU,但它是由线程池线程完成的,因为我使用 默认调度程序。理想情况下,线程应该只用于CPU绑定的应用程序 工作(实际有工作要做时)

对基于IO的操作使用PLINK/
Task.Run
/
Task.Factory.StartNew
是一种反模式。PLINQ(与
Parallel.For
etc相同)适用于CPU受限的计算工作,但为自然异步的网络/IO受限操作分配和阻塞线程是没有意义的,在“运行中”时根本不需要线程。为了遵循您展示的示例代码,这将类似于
new Ping().SendAsync(site)
,返回一个
任务。然后您可以执行
等待任务。当所有(任务)
并处理错误


斯蒂芬·克利里(Stephen Cleary)和他最近提出的最大并行IO度。除此之外,合并重试逻辑非常容易,而不涉及任何线程(例如,like)。

是的,本文的最后一个示例确实说明了如何使用非阻塞线程,但省略了WithDegreeOfParallism()这意味着它可能会导致数百个IO调用,而实际上我可能只需要5个未完成的IO请求。此外,PLINQ for IO可能是一种反模式,但他们通过提供WithDegreeOfParallelism()扩展鼓励反模式。我编辑了我的问题;请参阅我提供的链接,该链接用degreeofparallelism解释了并行性是为阻止IO而设计的。@Brent不要忘记,PLINQ是在异步等待之前设计的。@BrentArias,这对于客户端应用程序来说可能没问题,但是对于服务器端应用程序,每个请求加上5个线程可能很容易破坏可伸缩性。您可以编写“我可以使用Thread.Sleep()创建1分钟延迟,但我正在尝试不阻止线程。”那么,为什么您要使用同步IO调用阻止线程,而您可以使用
async/await
?这没什么不同。