Làm cách nào tôi có thể chuyển đổi mã foreach này thành Parallel.ForEach?


180

Tôi có một chút bối rối về Parallel.ForEach. Chính xác
Parallel.ForEachgì và nó làm gì?
Vui lòng không tham khảo bất kỳ liên kết MSDN.

Đây là một ví dụ đơn giản:

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);

foreach (string line in list_lines)
{
    //My Stuff
}

Làm thế nào tôi có thể viết lại ví dụ này với Parallel.ForEach?


Điều này có thể đã được trả lời ở đây stackoverflow.com/questions/3789998/ từ
Ujjwal Manandhar

1
@UjjwalManandhar Điều đó thực sự khá khác biệt, vì nó hỏi về sự khác biệt giữa Parallellớp và sử dụng PLINQ.
Sậy Copsey

18
Những người khác đã trả lời làm thế nào bạn có thể viết lại. Vì vậy, nó làm gì? Nó thực hiện một "hành động" trên mỗi mục trong bộ sưu tập, giống như một bình thường foreach. Sự khác biệt là phiên bản song song có thể thực hiện nhiều "hành động" cùng một lúc. Trong hầu hết các trường hợp (tùy thuộc vào máy tính nào đang chạy mã và mức độ bận rộn của nó và các công cụ khác), nó sẽ nhanh hơn và đó là lợi thế quan trọng nhất. Lưu ý rằng khi bạn thực hiện song song, bạn không thể biết thứ tự các mục được xử lý. Với một thông thường (nối tiếp) foreach, bạn được đảm bảo rằng lines[0]đến trước, sau đó lines[1], v.v.
Jeppe Stig Nielsen

1
@JeppeStigNielsen Nó sẽ không luôn luôn nhanh hơn vì có chi phí đáng kể với việc làm cho mọi thứ song song. Nó phụ thuộc vào kích thước của bộ sưu tập bạn đang lặp đi lặp lại và hành động bên trong. Điều chính xác cần làm là thực sự đo lường sự khác biệt giữa việc sử dụng Parallel.ForEach () và sử dụng foreach (). Nhiều lần một foreach bình thường () là nhanh hơn.
Dave Black

3
@DaveBlack Chắc chắn. Người ta sẽ phải đo xem nó nhanh hơn hay chậm hơn, trong từng trường hợp. Tôi chỉ cố gắng để mô tả song song nói chung.
Jeppe Stig Nielsen

Câu trả lời:


126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});

6
Chỉ muốn chỉ ra điều đó (nhiều hơn cho OP) để không có suy nghĩ sai lầm rằng nó chỉ hoạt động List<T>;)
Reed Copsey

1
cảm ơn sự quan tâm và trả lời tôi đã sử dụng Danh sách <chuỗi> trong mã của mình vì xóa các mục trùng lặp bằng danh sách HASH. với mảng thông thường, chúng tôi không thể loại bỏ trùng lặp một cách dễ dàng :).
SilverLight

119
Tôi bối rối rằng câu trả lời này được đánh dấu là câu trả lời đúng, vì không có lời giải thích cho bài viết câu hỏi ban đầu ... "Parallel.ForEach là gì và những gì nó chính xác làm gì?"
fose

6
@fosb Vấn đề là tiêu đề câu hỏi đã được chỉnh sửa để thay đổi hoàn toàn ý nghĩa ... vì vậy câu trả lời này không còn có ý nghĩa gì nữa. Có nói rằng, đó vẫn là một câu trả lời kém
aw04

274

Vòng lặp Foreach:

  • Lặp lại diễn ra tuần tự, từng cái một
  • vòng lặp foreach được chạy từ một Thread duy nhất.
  • vòng lặp foreach được định nghĩa trong mọi khung của .NET
  • Việc thực thi các quy trình chậm có thể chậm hơn , vì chúng chạy ổn định
    • Quá trình 2 không thể bắt đầu cho đến khi 1 hoàn thành. Quá trình 3 không thể bắt đầu cho đến khi 2 & 1 hoàn tất ...
  • Việc thực thi các quy trình nhanh có thể nhanh hơn , vì không có chi phí phân luồng

Song song.ForEach:

  • Thi công diễn ra theo cách song song.
  • Parallel.ForEach sử dụng nhiều Chủ đề.
  • Parallel.ForEach được định nghĩa trong .Net 4.0 trở lên.
  • Việc thực thi các quy trình chậm có thể nhanh hơn , vì chúng có thể được chạy song song
    • Các quy trình 1, 2, & 3 có thể chạy đồng thời (xem các chủ đề được sử dụng lại trong ví dụ, bên dưới)
  • Việc thực thi các quy trình nhanh có thể chậm hơn , do có thêm luồng xử lý

Ví dụ sau đây cho thấy rõ sự khác biệt giữa vòng lặp foreach truyền thống và

Ví dụ 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();
        }
    }
}

Đầu ra

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

Sử dụng ví dụ 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

63
Tôi không thực sự đồng ý với 'yêu cầu' của bạn rằng Parallel.ForEach (luôn luôn) nhanh hơn. Điều này thực sự phụ thuộc vào độ nặng của hoạt động bên trong vòng lặp. Điều này có thể hoặc không có giá trị chi phí cho việc giới thiệu paralellism.
Martao

1
Vâng, song song cho mỗi phương tiện mà các luồng riêng biệt được thiết lập để thực thi mã trong thân vòng lặp. Mặc dù .NET có cơ chế hiệu quả để làm việc này, nhưng đây là chi phí đáng kể. Vì vậy, nếu bạn chỉ phải thực hiện một thao tác đơn giản (ví dụ: tổng hoặc nhân), thì việc tìm kiếm song song không nên nhanh hơn.
Martao

3
@Jignesh đây thậm chí không phải là ví dụ đo lường tốt nên tôi hoàn toàn không đề cập đến điều này. Xóa "Thread.S ngủ (10);" từ mỗi thân vòng lặp và thử lại.
mạnh mẽ vào

1
@Martao đã đúng, vấn đề là với các chi phí khóa đối tượng trong đó cách tiếp cận song song có thể dài hơn tuần tự.
mạnh mẽ vào

8
@stenly Tôi nghĩ rằng Giấc ngủ chính xác là lý do tại sao nó là một ví dụ tốt . Bạn sẽ không sử dụng PFE với các lần lặp đơn nhanh (như Martao đã giải thích) - vì vậy câu trả lời này làm cho việc lặp lại chậm và lợi thế (chính xác) của PFE được nêu bật. Tôi đồng ý mặc dù điều này cần được giải thích trong câu trả lời, một chữ "luôn luôn nhanh hơn" rất sai lệch.
mafu

43
string[] lines = File.ReadAllLines(txtProxyListPath.Text);

// No need for the list
// List<string> list_lines = new List<string>(lines); 

Parallel.ForEach(lines, line =>
{
    //My Stuff
});

Điều này sẽ khiến các dòng được phân tích song song, trong vòng lặp. Nếu bạn muốn giới thiệu chi tiết hơn, ít "định hướng tham chiếu" hơn cho lớp Parallel, tôi đã viết một loạt bài về TPL bao gồm một phần trên Parallel.ForEach .


9

Đối với tệp lớn, hãy sử dụng đoạn mã sau (bạn sẽ ít đói hơn)

Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line => {
    //Your stuff
});

2

Những dòng này làm việc cho tôi.

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.