【问题标题】:Parallel I/O and Retry Logic for Error Handling用于错误处理的并行 I/O 和重试逻辑
【发布时间】:2014-04-09 22:35:51
【问题描述】:

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

想法?

【问题讨论】:

    标签: c# multithreading error-handling io plinq


    【解决方案1】:

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

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

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

    【讨论】:

    • 是的,文章的最后一个示例确实展示了如何拥有非阻塞线程,但它省略了 WithDegreeOfParallism() 这意味着它可能会导致进行数百次 IO 调用,而事实上我可能只想要并且正好有 5 个未完成的 IO 请求。此外,PLINQ for IO 可能是一种反模式,但他们通过提供 WithDegreeOfParallelism() 扩展来鼓励这种反模式。我编辑了我的问题;请参阅我提供的链接,该链接解释了 WithDegreeOfParallelism 是为阻塞 IO 而设计的。
    • @Brent 不要忘记 PLINQ 是在 async-await 之前设计的。
    • @BrentArias,这对于客户端应用程序来说可能没问题,但对于服务器端应用程序,每个请求多 5 个线程可能很容易破坏可伸缩性。你写 “我可以用 Thread.Sleep() 创建 1 分钟的延迟,但我试图不阻塞线程。” 那么你为什么要用同步 IO 调用来阻塞它们,而你可以使用async/await?这并没有太大的不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2018-05-20
    • 2019-04-15
    • 2010-09-22
    • 1970-01-01
    相关资源
    最近更新 更多