如何在Thread-Local状态下使用Parallel.ForEach?

时间:2015-07-26 10:12:44

标签: c# multithreading parallel-processing thread-local-storage

问题:我在文章中看到了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?查看"线程本地状态"章。

2 个答案:

答案 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无法知道您的网络可以支持多少并发请求。