C# 如何将Parallel.ForEach与线程本地状态一起使用?
问题:我在一篇文章中看到了一个C# 如何将Parallel.ForEach与线程本地状态一起使用?,c#,multithreading,parallel-processing,thread-local-storage,C#,Multithreading,Parallel Processing,Thread Local Storage,问题:我在一篇文章中看到了一个Parallel.Foreach()的两个实现使用WebCLient下载URL。作者建议,在第一个示例中,如果我们有一个100个URL的数组,那么将启动100个WebClient,其中大多数将超时。因此,他提出了第二个实现,其中他使用了线程本地状态,并表示“将根据需要生成尽可能多的WebClient()对象” 问题:第二个示例如何确保不会发生超时?或者换句话说,第二个示例如何考虑连接的局部限制?客户端会被重用吗 来源: 注意:在多个线程上生成WebClient仅用
Parallel.Foreach()的两个实现
使用WebCLient
下载URL。作者建议,在第一个示例中,如果我们有一个100个URL的数组,那么将启动100个WebClient,其中大多数将超时。因此,他提出了第二个实现,其中他使用了线程本地状态,并表示“将根据需要生成尽可能多的WebClient()对象”
问题:第二个示例如何确保不会发生超时?或者换句话说,第二个示例如何考虑连接的局部限制?客户端会被重用吗
来源:
注意:在多个线程上生成WebClient仅用于演示目的。我知道异步操作会更有效
链接我从中获得源代码(我简化了一点):看看“线程本地状态”一章
换句话说,第二个例子如何考虑
本地连接限制?客户端会被重用吗
第二个示例所做的是,它不是在每次迭代中创建WebClient
对象,而是在每个线程中创建WebClient
。这意味着如果Parallel.ForEach
使用4个线程,它将创建4个实例,并在迭代之间重用这些对象。因此,能够重新使用每个客户端创建的连接,而不是新实例,而新实例又必须等待所有其他客户端连接关闭
最终,所有客户端都在争夺通过底层ServicePointManager.DefaultConnectionLimit
提供的相同IO资源。打开的连接越少,完成每个请求的时间就越多。这也可以通过增加允许的连接限制数量(默认为2)来解决
一般来说,不需要使用多个线程来执行并发IO请求。并行性在这里实际上没有帮助。通过使用线程本地状态,我们现在每个线程有一个WebClient。每次迭代都没有一个客户机 作者的想法是,我们现在有更少的网络客户端的浮动和消耗资源。这个论点是假的,因为目前没有执行任何调用的WebClient实例不会占用任何资源。Dispose在WebClient上不执行任何操作。用包装纸把它包起来,你就完成了
您需要在这里使用PLINQ,因为Parallel容易产生无限数量的线程。使用IO时,您需要自己控制DOP。只有使用PLINQ才能设置精确的DOP。TPL不知道您的网络可以支持多少并发请求。但在第一种情况下,也会为每个请求创建
WebClient
。因此。。第二个示例如何限制并行连接的数量?因此,在第二个示例中,如果parallel.Foreach()
启动更多操作系统能够处理的连接,它们也会超时,对吗?优化只是为了重用客户端,示例不会阻止超时?@f0rt-Yes。如果理论上Parallel.ForEach
使用了100个线程,那么就有100个WebClient
实例。据我所知,这两个实例都支持DOP:-PLINQ via WithDegreeOfParallelism()方法-Parallel.ForEach()via parallellel可枚举。WithDegreeOfParallelism唯一的区别是并行。ForEach()可以使用少于指定的数量。不管怎样,我明白你说的。谢谢。:)对。这意味着WithDegreeOfParallelism可能使用一个线程来驱动网络,这对性能来说是灾难性的。好吧,这个评论还不清楚。在单核计算机上,虽然指定了20,但可能会得到一个线程事件。
// First example
Parallel.ForEach(urls,
(url,loopstate,index) =>
{
WebClient webclient = new WebClient();
webclient.DownloadFile(url, filenames[index];
});
// Second example
Parallel.ForEach(urls,
() => new WebClient(),
(url, loopstate, index, webclient) =>
{
webclient.DownloadFile(url, filenames[index]);
},
(webclient) => { });