问题:我在文章中看到了Parallel.Foreach()
个WebCLient
下载网址的// 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) => { });
实施情况。作者建议在第一个例子中,如果我们有一个100个网址的数组 - 将启动100个WebClient,其中大多数将超时。所以他提出了第二个实现,他使用了线程本地状态,他说" 因为我们需要"所以会产生许多WebClient()对象。
问题:第二个示例如何确保不会发生超时?或者换句话说,第二个例子如何考虑连接的局部限制?客户会被重用吗?
来源:
{{1}}
注意:在多个线程上生成WebClient仅用于演示目的。我知道使用异步操作会更有效。
链接我从中得到了源代码(我简化了一点):When Should I Use Parallel.ForEach? When Should I Use PLINQ?查看"线程本地状态"章。
答案 0 :(得分:1)
换句话说,第二个例子是如何考虑到的 当地的连接限制?客户会被重用吗?
第二个示例的作用是,不是每次迭代创建一个WebClient
对象,而是为每个线程创建一个WebClient
。这意味着如果Parallel.ForEach
使用4个线程,它将创建4个实例,并将在迭代之间重用这些对象。因此,能够重新使用每个客户端创建的连接而不是新实例,而新实例又必须等待所有其他客户端连接关闭。
最终,所有客户都在争夺通过基础ServicePointManager.DefaultConnectionLimit
提供的相同IO资源。您打开的连接越少,每个请求完成执行的时间就越多。这也可以通过增加允许的连接限制数来解决,默认为2。
一般来说,不需要使用多个线程来执行并发IO请求。并行性实际上并没有帮助。
答案 1 :(得分:1)
通过使用线程局部状态,我们现在每个线程有一个WebClient。每次迭代不是一个客户端。
作者的想法是,我们现在有更少的WebClient浮动并消耗资源。这个论点是假的,因为目前没有执行任何调用的WebClient实例不会占用任何资源。 Dispose对WebClient没有任何作用。将它包裹在使用中,你就完成了。
你需要在这里使用PLINQ,因为Parallel很容易产生无限数量的线程。使用IO,您需要自己控制DOP。只有使用PLINQ才能设置精确的DOP。 TPL无法知道您的网络可以支持多少并发请求。