如何将此 foreach 代码转换为 Parallel.ForEach?
Posted
技术标签:
【中文标题】如何将此 foreach 代码转换为 Parallel.ForEach?【英文标题】:How can I convert this foreach code to Parallel.ForEach? 【发布时间】:2012-08-28 10:16:05 【问题描述】:我对@987654322@ 有点困惑。Parallel.ForEach
是什么,它到底有什么作用?
请不要引用任何 MSDN 链接。
这是一个简单的例子:
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
foreach (string line in list_lines)
//My Stuff
我怎样才能用Parallel.ForEach
重写这个例子?
【问题讨论】:
这里可能已经回答了***.com/questions/3789998/… @UjjwalManandhar 这实际上完全不同,因为它询问Parallel
类和使用 PLINQ 之间的区别。
其他人已经回答了如何重写。那么它有什么作用呢?它对集合中的每个项目执行“操作”,就像普通的 foreach
一样。不同的是,并行版本可以同时做很多“动作”。在大多数情况下(取决于运行代码的计算机、它的繁忙程度以及其他因素)它会更快,这是最重要的优势。请注意,当您并行执行时,您无法知道处理项目的顺序。使用通常的(串行)foreach
,您可以保证首先出现lines[0]
,然后是lines[1]
,依此类推。
@JeppeStigNielsen 它不会总是更快,因为并行处理会产生大量开销。这取决于您正在迭代的集合的大小和其中的操作。正确的做法是实际测量使用 Parallel.ForEach() 和使用 foreach() 之间的差异。很多时候,普通的 foreach() 更快。
@DaveBlack 当然。在每种情况下,都必须衡量它是更快还是更慢。我只是想概括地描述并行化。
【参考方案1】:
Foreach 循环:
迭代按顺序进行,一个接一个 foreach 循环从单个线程运行。 .NET 的每个框架中都定义了 foreach 循环 slow 进程的执行可能会较慢,因为它们是连续运行的 进程 2 在 1 完成之前无法启动。流程 3 无法启动,直到 2 和 1 完成... 快速进程的执行可以更快,因为没有线程开销
Parallel.ForEach:
以并行方式执行。 Parallel.ForEach 使用多个线程。 Parallel.ForEach 在 .Net 4.0 及更高版本的框架中定义。 慢进程的执行可以更快,因为它们可以并行运行 进程 1、2 和 3可能同时运行(请参阅下面的示例中的重用线程) 由于额外的线程开销,快速进程的执行可能较慢
下面的例子清楚地展示了传统foreach循环与
的区别Parallel.ForEach() 示例
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelForEachExample
class Program
static void Main()
string[] colors =
"1. Red",
"2. Green",
"3. Blue",
"4. Yellow",
"5. White",
"6. Black",
"7. Violet",
"8. Brown",
"9. Orange",
"10. Pink"
;
Console.WriteLine("Traditional foreach loop\n");
//start the stopwatch for "for" loop
var sw = Stopwatch.StartNew();
foreach (string color in colors)
Console.WriteLine("0, Thread Id= 1", color, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
Console.WriteLine("foreach loop execution time = 0 seconds\n", sw.Elapsed.TotalSeconds);
Console.WriteLine("Using Parallel.ForEach");
//start the stopwatch for "Parallel.ForEach"
sw = Stopwatch.StartNew();
Parallel.ForEach(colors, color =>
Console.WriteLine("0, Thread Id= 1", color, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
);
Console.WriteLine("Parallel.ForEach() execution time = 0 seconds", sw.Elapsed.TotalSeconds);
Console.Read();
输出
Traditional foreach loop
1. Red, Thread Id= 10
2. Green, Thread Id= 10
3. Blue, Thread Id= 10
4. Yellow, Thread Id= 10
5. White, Thread Id= 10
6. Black, Thread Id= 10
7. Violet, Thread Id= 10
8. Brown, Thread Id= 10
9. Orange, Thread Id= 10
10. Pink, Thread Id= 10
foreach loop execution time = 0.1054376 seconds
使用 Parallel.ForEach 示例
1. Red, Thread Id= 10
3. Blue, Thread Id= 11
4. Yellow, Thread Id= 11
2. Green, Thread Id= 10
5. White, Thread Id= 12
7. Violet, Thread Id= 14
9. Orange, Thread Id= 13
6. Black, Thread Id= 11
8. Brown, Thread Id= 10
10. Pink, Thread Id= 12
Parallel.ForEach() execution time = 0.055976 seconds
【讨论】:
我不太同意您的“声称”,即 Parallel.ForEach (总是)更快。这实际上取决于循环内部操作的繁重程度。这可能值得也可能不值得引入并行性的开销。 好吧,每个线程的并行意味着设置单独的线程来执行循环体中的代码。尽管 .NET 确实具有执行此操作的有效机制,但这是相当大的开销。因此,如果您只需要一个简单的操作(例如求和或乘法),并行 foreach 应该不会更快。 @Jignesh 这甚至不是很好的测量示例,所以我根本不会提到这个。删除“Thread.Sleep(10);”从每个循环体中再试一次。 @Martao 是对的,问题在于对象锁定开销,其中并行方法可能比顺序方法更长。 @stenly 我认为睡眠正是它成为好示例的原因。您不会使用具有快速单次迭代的 PFE(正如 Martao 解释的那样) - 所以这个答案使迭代变慢,并且突出了 PFE 的(正确)优势。我同意虽然这需要在答案中解释,但粗体的“总是更快”非常具有误导性。【参考方案2】:string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
//Your stuff
);
【讨论】:
只是想指出这一点(对于 OP 来说更多),以免误以为它只适用于List<T>
;)
感谢您的关注和回答。我在代码中使用了 Liststring[] lines = File.ReadAllLines(txtProxyListPath.Text);
// No need for the list
// List<string> list_lines = new List<string>(lines);
Parallel.ForEach(lines, line =>
//My Stuff
);
这将导致在循环内并行解析行。如果您想要更详细、更少“面向参考”的 Parallel 类介绍,我写了一个关于 TPL 的系列文章,其中包括 section on Parallel.ForEach。
【讨论】:
【参考方案4】:对于大文件,请使用以下代码(您的内存消耗较少)
Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line =>
//Your stuff
);
【讨论】:
【参考方案5】:这些行对我有用。
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions MaxDegreeOfParallelism = Environment.ProcessorCount * 10 ;
Parallel.ForEach(lines , options, (item) =>
//My Stuff
);
【讨论】:
【参考方案6】:我想补充一下并行选项。如果您没有提到它,默认情况下所有 RAM 都将用于此,这可能会给您带来生产问题。所以最好在代码中添加最大并行度。
Parallel.ForEach(list_lines, new ParallelOptions MaxDegreeOfParallelism = 2 , line =>
);
【讨论】:
以上是关于如何将此 foreach 代码转换为 Parallel.ForEach?的主要内容,如果未能解决你的问题,请参考以下文章