如何在 Thread-Local 状态下使用 Parallel.ForEach?
How to use Parallel.ForEach with Thread-Local state?
问题: 我在一篇文章中看到 Parallel.Foreach()
下载 url 与 WebCLient
的 2 个实现。作者建议,在第一个示例中,如果我们有一个包含 100 个 url 的数组 - 将启动 100 个 WebClient,其中大部分将超时。所以他提出了第二个实现,他使用线程本地状态,他说“将根据我们的需要生成尽可能多的 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) => { });
注意: 在多线程上生成 WebClient 仅用于演示目的。我知道异步操作会更有效。
Link 我从中获取了源代码(我稍微简化了它):When Should I Use Parallel.ForEach? When Should I Use PLINQ? 看看 "Thread-Local state" 章节。
in other words how the second example takes in consideration the
local limit of connections? Will the clients be reused or something?
第二个示例所做的是,它不是在每次迭代中创建一个 WebClient
对象,而是在每个线程 中创建一个 WebClient
而不是 。这意味着如果 Parallel.ForEach
使用 4 个线程,它将创建 4 个实例并在迭代之间重用这些对象。因此,能够重新使用每个客户端创建的连接而不是新实例,而新实例又必须等待所有其他客户端连接关闭。
最终,所有客户端都在争夺通过底层 ServicePointManager.DefaultConnectionLimit
可用的相同 IO 资源。打开的连接越少,每个请求完成执行的时间就越多。这也可以通过增加允许的连接限制数量来解决,默认为 2。
一般来说,没有必要使用多线程来执行并发IO请求。并行性实际上在这里没有帮助。
通过使用线程本地状态,我们现在每个线程都有一个 WebClient。每次迭代不是一个客户。
作者的想法是我们现在有更少的 WebClient 浮动和消耗资源。该论点是虚假的,因为此时未执行任何调用的 WebClient 实例不会占用任何资源。 Dispose 在 WebClient 上不执行任何操作。将它包装在 using 中,你就完成了。
您需要在这里使用 PLINQ,因为 Parallel 很容易产生无限数量的线程。使用 IO,您需要自己控制 DOP。只有使用 PLINQ 才能设置准确的 DOP。 TPL 无法知道您的网络可以承受多少个并发请求。
问题: 我在一篇文章中看到 Parallel.Foreach()
下载 url 与 WebCLient
的 2 个实现。作者建议,在第一个示例中,如果我们有一个包含 100 个 url 的数组 - 将启动 100 个 WebClient,其中大部分将超时。所以他提出了第二个实现,他使用线程本地状态,他说“将根据我们的需要生成尽可能多的 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) => { });
注意: 在多线程上生成 WebClient 仅用于演示目的。我知道异步操作会更有效。
Link 我从中获取了源代码(我稍微简化了它):When Should I Use Parallel.ForEach? When Should I Use PLINQ? 看看 "Thread-Local state" 章节。
in other words how the second example takes in consideration the local limit of connections? Will the clients be reused or something?
第二个示例所做的是,它不是在每次迭代中创建一个 WebClient
对象,而是在每个线程 中创建一个 WebClient
而不是 。这意味着如果 Parallel.ForEach
使用 4 个线程,它将创建 4 个实例并在迭代之间重用这些对象。因此,能够重新使用每个客户端创建的连接而不是新实例,而新实例又必须等待所有其他客户端连接关闭。
最终,所有客户端都在争夺通过底层 ServicePointManager.DefaultConnectionLimit
可用的相同 IO 资源。打开的连接越少,每个请求完成执行的时间就越多。这也可以通过增加允许的连接限制数量来解决,默认为 2。
一般来说,没有必要使用多线程来执行并发IO请求。并行性实际上在这里没有帮助。
通过使用线程本地状态,我们现在每个线程都有一个 WebClient。每次迭代不是一个客户。
作者的想法是我们现在有更少的 WebClient 浮动和消耗资源。该论点是虚假的,因为此时未执行任何调用的 WebClient 实例不会占用任何资源。 Dispose 在 WebClient 上不执行任何操作。将它包装在 using 中,你就完成了。
您需要在这里使用 PLINQ,因为 Parallel 很容易产生无限数量的线程。使用 IO,您需要自己控制 DOP。只有使用 PLINQ 才能设置准确的 DOP。 TPL 无法知道您的网络可以承受多少个并发请求。