ฉันจะแปลงรหัส foreach นี้เป็น Parallel.ForEach ได้อย่างไร


180

ฉันสับสนParallel.ForEachเล็กน้อย
มันคืออะไร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อย่างไร


นี่อาจจะได้รับคำตอบที่นี่stackoverflow.com/questions/3789998/…
Ujjwal Manandhar

1
@UjjwalManandhar ที่จริงแล้วมันค่อนข้างแตกต่างเพราะถามถึงความแตกต่างระหว่างParallelชั้นเรียนกับการใช้ PLINQ
Reed Copsey

18
คนอื่น ๆ ตอบว่าคุณเขียนได้อย่างไร แล้วมันจะทำอย่างไร? มันจะเป็น "การกระทำ" foreachในแต่ละรายการในคอลเลกชันเหมือนปกติ ความแตกต่างคือรุ่นขนานสามารถทำ "การกระทำ" ได้หลายอย่างในเวลาเดียวกัน ในกรณีส่วนใหญ่ (ขึ้นอยู่กับคอมพิวเตอร์ที่ใช้รหัสและสิ่งที่ยุ่งและสิ่งอื่น ๆ ) มันจะเร็วขึ้นและนั่นเป็นข้อได้เปรียบที่สำคัญที่สุด ทราบว่าเมื่อคุณทำมันในแบบคู่ขนานคุณไม่สามารถรู้ในสิ่งที่สั่งซื้อรายการที่มีการประมวลผล ด้วยปกติ (ซีเรียล) foreachคุณจะได้รับการรับประกันที่lines[0]มาก่อนlines[1]และอื่น ๆ
Jeppe Stig Nielsen

1
@JeppeStigNielsen มันจะไม่เร็วขึ้นเสมอเนื่องจากมีค่าใช้จ่ายที่สำคัญเมื่อทำสิ่งต่าง ๆ แบบขนาน ขึ้นอยู่กับขนาดของคอลเลกชันที่คุณกำลังทำซ้ำและการกระทำภายใน สิ่งที่ถูกต้องคือการวัดความแตกต่างระหว่างการใช้ Parallel.ForEach () และใช้ foreach () หลายครั้งที่ foreach ปกติ () เร็วขึ้น
เดฟแบล็ค

3
@DaveBlack แน่นอน หนึ่งจะต้องวัดว่ามันจะเร็วขึ้นหรือช้าลงในแต่ละกรณี ฉันแค่พยายามอธิบายการขนานกันโดยทั่วไป
Jeppe Stig Nielsen

คำตอบ:


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

6
แค่ต้องการชี้ให้เห็น (เพิ่มเติมสำหรับ OP) เพื่อให้ไม่มีความคิดที่เข้าใจผิดว่าใช้ได้ผลเท่านั้นList<T>)
Reed Copsey

1
ขอบคุณสำหรับความสนใจและคำตอบ ฉันใช้ List <string> ในรหัสของฉันเนื่องจากลบรายการที่ซ้ำกันโดยใช้รายการ HASH ด้วยอาร์เรย์ปกติเราไม่สามารถลบรายการที่ซ้ำกันได้อย่างง่ายดาย :)
SilverLight

119
ฉันสับสนว่าคำตอบนี้ถูกทำเครื่องหมายเป็นคำตอบที่ถูกต้องเนื่องจากไม่มีคำอธิบายสำหรับคำถามโพสต์ดั้งเดิม "What is Parallel.ForEach และมันทำอะไรกันแน่?" ...
fose

6
@fosb ปัญหาคือชื่อคำถามถูกแก้ไขเพื่อเปลี่ยนความหมายอย่างสมบูรณ์ ... ดังนั้นคำตอบนี้ไม่สมเหตุสมผลอีกต่อไป ต้องบอกว่ามันยังคงเป็นคำตอบที่ไม่ดี
aw04

274

วงหน้า:

  • การทำซ้ำจะเกิดขึ้นตามลำดับทีละคน
  • foreach loop ถูกเรียกใช้จากเธรดเดี่ยว
  • foreach loop ถูกกำหนดไว้ในทุก ๆ Framework ของ. NET
  • การดำเนินการกระบวนการที่ช้าอาจช้าลงได้เนื่องจากพวกเขาทำงานแบบต่อเนื่อง
    • กระบวนการ 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

63
ฉันไม่เห็นด้วยกับ 'การเรียกร้อง' ของคุณที่ Parallel.ForEach เร็วกว่าเสมอ สิ่งนี้ขึ้นอยู่กับความหนักเบาของการทำงานภายในลูป สิ่งนี้อาจจะใช่หรือไม่คุ้มกับค่าใช้จ่ายของการแนะนำอัมพาต
Martao

1
การขนานกันของแต่ละวิธีนั้นมีการตั้งค่าเธรดแยกต่างหากเพื่อเรียกใช้งานโค้ดในวนลูป แม้ว่า. NET จะมีกลไกที่มีประสิทธิภาพในการทำสิ่งนี้ ดังนั้นหากคุณเพียงแค่ทำการดำเนินการอย่างง่าย ๆ (เช่นผลรวมหรือการคูณ) foreach แบบขนานไม่ควรเร็วขึ้น
Martao

3
@ Jignesh นี่ไม่ใช่ตัวอย่างการวัดที่ดีดังนั้นฉันจะไม่พูดถึงเรื่องนี้เลย ลบ "Thread.Sleep (10);" จากร่างกายแต่ละวงแล้วลองอีกครั้ง
stenly

1
@Martao ถูกต้องปัญหาอยู่ที่การล็อกวัตถุค่าใช้จ่ายซึ่งวิธีการแบบขนานอาจยาวกว่าตามลำดับ
stenly

8
@ อย่างแท้จริงฉันคิดว่าการนอนหลับเป็นเหตุผลที่แม่นยำว่าเป็นตัวอย่างที่ดี คุณจะไม่ใช้ PFE กับการทำซ้ำเดี่ยวอย่างรวดเร็ว (ดังที่ Martao อธิบาย) - ดังนั้นคำตอบนี้ทำให้การทำซ้ำช้าและความได้เปรียบ (ถูกต้อง) ของ PFE จะถูกเน้น ฉันเห็นด้วยแม้ว่าสิ่งนี้จะต้องอธิบายในคำตอบตัวหนา "เร็วกว่าเสมอ" นั้นทำให้เข้าใจผิดมาก
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
});

สิ่งนี้จะทำให้บรรทัดถูกแยกวิเคราะห์แบบขนานภายในลูป หากคุณต้องการรายละเอียดเพิ่มเติมน้อย "ที่มุ่งเน้นการอ้างอิง" แนะนำให้รู้จักกับระดับขนานผมเขียนชุดใน TPL ซึ่งรวมถึงการเป็นส่วน Parallel.ForEach


9

สำหรับไฟล์ขนาดใหญ่ใช้รหัสต่อไปนี้ (คุณหิวหน่วยความจำน้อยกว่า)

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

2

สายเหล่านี้ใช้ได้สำหรับฉัน

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.