ความแตกต่างระหว่างงานและด้ายคืออะไร?


378

ใน C # 4.0 เรามีTaskในSystem.Threading.Tasks namespace ความแตกต่างที่แท้จริงระหว่างThreadและTaskคืออะไร ฉันทำโปรแกรมตัวอย่าง (ความช่วยเหลือจาก MSDN) เพื่อการเรียนรู้ด้วยตัวเอง

Parallel.Invoke 
Parallel.For 
Parallel.ForEach 

แต่มีข้อสงสัยมากมายเนื่องจากแนวคิดไม่ชัดเจน

ฉันเคยค้นหาใน Stackoverflow สำหรับคำถามประเภทเดียวกัน แต่อาจอยู่กับชื่อคำถามนี้ฉันไม่สามารถได้รับเหมือนกัน หากใครรู้เกี่ยวกับคำถามประเภทเดียวกันที่โพสต์ไว้ที่นี่ก่อนหน้านี้โปรดให้ข้อมูลอ้างอิงจากลิงก์


8
งานที่ทำงานหัวข้อ
pm100

คำตอบ:


314

งานคือสิ่งที่คุณต้องการทำ

เธรดเป็นหนึ่งในผู้ปฏิบัติงานที่เป็นไปได้จำนวนมากซึ่งทำงานนี้

ในเงื่อนไข. NET 4.0 งานหมายถึงการดำเนินการแบบอะซิงโครนัส กระทู้ถูกนำมาใช้เพื่อให้การดำเนินการนั้นเสร็จสมบูรณ์โดยแบ่งงานออกเป็นชิ้น ๆ และกำหนดให้กับเธรดแยกกัน


คุณสามารถให้ตัวอย่างพื้นฐานของเธรดที่ทำงานเพื่อให้งานสำเร็จหรือไม่? ฉันไม่รู้ว่าเธรดกำลังทำงานที่อิสระระหว่างกันหรือพวกเขาทำการคำนวณการทำงานเป็นทีมบ้างไหม?
pensum

ทั้งสองสถานการณ์เป็นไปได้: ในสถานการณ์ที่เหมาะสมเธรดจะทำงานอย่างอิสระโดยไม่จำเป็นต้องซิงโครไนซ์กับเธรดอื่น ในทางปฏิบัติล็อคถูกใช้เพื่อประสานงานเธรด
มิทช์ข้าวสาลี

451

ในแง่วิทยาศาสตร์คอมพิวเตอร์ที่Taskเป็นอนาคตหรือสัญญา (บางคนใช้คำสองคำนี้อย่างกลมกลืนบางคนใช้พวกมันแตกต่างกันไม่มีใครสามารถเห็นด้วยกับคำจำกัดความที่แม่นยำ ) โดยทั่วไปTask<T>"สัญญา" ที่จะส่งคืนคุณTแต่ไม่ใช่ตอนนี้ที่รักฉันค่อนข้างยุ่งทำไมไม่ คุณจะกลับมาใหม่ในภายหลัง?

A Threadเป็นวิธีปฏิบัติตามสัญญานั้น แต่ไม่ทุกความต้องการใหม่แบรนด์Task Thread(อันที่จริงแล้วการสร้างเธรดมักไม่เป็นที่ต้องการเนื่องจากการทำเช่นนั้นมีราคาแพงกว่าการใช้เธรดที่มีอยู่อีกครั้งจากเธรดพูลอีกครั้งในอีกสักครู่) ถ้าค่าที่คุณรอมาจากระบบไฟล์หรือ ฐานข้อมูลหรือเครือข่ายจากนั้นไม่จำเป็นต้องมีเธรดเพื่อรอและรอข้อมูลเมื่อสามารถให้บริการคำขออื่น ๆ ได้ แต่ผู้ใช้Taskอาจลงทะเบียนการโทรกลับเพื่อรับค่าเมื่อพร้อม

โดยเฉพาะอย่างยิ่งTaskไม่ได้บอกว่าทำไมมันถึงใช้เวลานานเช่นในการส่งคืนค่า มันอาจเป็นไปได้ว่ามันต้องใช้เวลานานในการคำนวณหรือมันอาจจะว่ามันต้องใช้เวลานานในการดึงข้อมูล เฉพาะในกรณีที่คุณจะใช้ในการทำงานThread Task(ใน. NET, เธรดมีราคาแพงดังนั้นโดยทั่วไปคุณต้องการหลีกเลี่ยงมากที่สุดเท่าที่จะเป็นไปได้และใช้จริง ๆ ถ้าคุณต้องการเรียกใช้การคำนวณจำนวนมากหลายครั้งบน CPU หลายตัวตัวอย่างเช่นใน Windows เธรดมีน้ำหนัก 12 KiByte ( ฉันคิดว่า) ใน Linux เธรดมีน้ำหนักเพียง 4 KiByte ใน Erlang / BEAM แม้เพียง 400 Byte ใน NET. เป็น 1 MiByte!)


29
น่าสนใจในการแสดงตัวอย่างก่อนหน้าของ TPL (Task Parallel Library) มีงานและอนาคต <T> เปลี่ยน <T> ในอนาคตเป็น <T> ของงานแล้ว :)
Lee Campbell

23
คุณคำนวณ 1 MB สำหรับ. NET อย่างไร
dvallejo

5
@DanVallejo: ถูกกล่าวถึงในการสัมภาษณ์กับทีมออกแบบของ TPL ฉันไม่สามารถบอกคุณได้ว่าใครเป็นคนพูดหรือให้สัมภาษณ์แบบไหนฉันดูเมื่อหลายปีก่อน
Jörg W Mittag

9
@RIPUNJAYTRIPATHI แน่นอน แต่ไม่จำเป็นต้องเป็นเธรดอื่นอาจเป็นเธรดที่ร้องของานในตอนแรก
Chris Pitman

7
.NET เพียงใช้เธรด Windows บน Windows ดังนั้นขนาดจึงเหมือนกัน - โดยค่าเริ่มต้นคือ 1 MiB ของหน่วยความจำเสมือนสำหรับทั้งคู่ หน่วยความจำกายภาพใช้ตามความจำเป็นในชิ้นขนาดหน้ากระดาษ (โดยปกติคือ 64 kiB) ซึ่งเหมือนกับโค้ดท้องถิ่น ขนาดสแต็กของเธรดขั้นต่ำขึ้นอยู่กับ OS - 256 kiB สำหรับ Vista ตัวอย่างเช่น บน x86 Linux ค่าเริ่มต้นมักเป็น 2 MiB - อีกครั้งจัดสรรเป็นชิ้นขนาดหน้า (การทำให้เข้าใจง่าย) Erlang ใช้เพียงหนึ่งเธรดระบบต่อกระบวนการ 400 ไบต์เหล่านั้นอ้างถึงสิ่งที่คล้ายกับTask. NET
Luaan

39

เกลียว

สิ่งที่เป็นโลหะเปลือยคุณอาจไม่จำเป็นต้องใช้มันคุณอาจใช้LongRunningงานและรับประโยชน์จาก TPL - Task Parallel Library ซึ่งรวมอยู่ใน. NET Framework 4 (กุมภาพันธ์, 2002) และข้างบน (เช่น. NET แกน)

งาน

สิ่งที่เป็นนามธรรมเหนือเธรด มันใช้เธรดพูล (ยกเว้นว่าคุณระบุงานเป็นการLongRunningดำเนินการถ้าเป็นเช่นนั้นเธรดใหม่จะถูกสร้างขึ้นภายใต้ประทุนสำหรับคุณ)

เธรดพูล

ตามที่ชื่อแนะนำ: กลุ่มของเธรด . NET Framework จัดการกับเธรดจำนวน จำกัด สำหรับคุณหรือไม่ ทำไม? เพราะการเปิด 100 เธรดเพื่อดำเนินการกับ CPU ราคาแพงบนโปรเซสเซอร์ที่มีเพียง 8 คอร์แน่นอนว่าไม่ใช่ความคิดที่ดี เฟรมเวิร์กจะดูแลกลุ่มนี้ให้คุณนำเธรด (ไม่สร้าง / ฆ่าพวกเขาในแต่ละการดำเนินการ) และดำเนินการบางส่วนในแบบคู่ขนานในลักษณะที่ CPU ของคุณจะไม่เบิร์น

ตกลง แต่เมื่อไหร่ที่จะใช้แต่ละอัน?

ในประวัติย่อ: ใช้งานเสมอ

งานเป็นสิ่งที่เป็นนามธรรมดังนั้นจึงง่ายต่อการใช้งานมาก ฉันแนะนำให้คุณลองใช้งานและหากคุณประสบปัญหาที่ทำให้คุณต้องจัดการเธรดด้วยตัวเอง (อาจเป็น 1% ของเวลา) จากนั้นใช้เธรด

แต่ระวังว่า:

  • I / O Bound : สำหรับการดำเนินการที่ถูกผูกไว้ของ I / O (การโทรฐานข้อมูล, ไฟล์อ่าน / เขียน, การเรียก APIs ฯลฯ ) หลีกเลี่ยงการใช้งานปกติ, ใช้LongRunningงาน ( หรือเธรดหากคุณต้องการ ) เนื่องจากการใช้งานจะนำคุณไปสู่เธรดพูลต์ที่มีเธรดไม่ว่างจำนวนมากและมีงานอื่นอีกมากที่รอการเปิดใช้งานพูล
  • CPU Bound : สำหรับการดำเนินการที่เชื่อมโยงกับ CPU เพียงใช้งานปกติ (ซึ่งจะใช้เธรดพูลภายใน) และมีความสุข

การแก้ไขเล็กน้อยเธรดไม่ใช่ "สิ่งที่เป็นโลหะเปลือย" มันถูกนำมาใช้โดยระบบปฏิบัติการส่วนใหญ่ใช้งานการถ่ายทอดในคุณสมบัติของ CPU และ CS แต่พวกเขาไม่ได้ดำเนินการโดยฮาร์ดแวร์
Tomer W

7

คุณสามารถใช้Taskเพื่อระบุสิ่งที่คุณต้องการจะทำแล้วแนบที่มีTask Threadเพื่อที่Taskจะถูกดำเนินการในสิ่งที่ทำขึ้นมาใหม่Threadแทนที่จะอยู่บนเธรด GUI

ใช้กับTask TaskFactory.StartNew(Action action)ในที่นี้คุณจะทำการมอบหมายงานดังนั้นหากคุณไม่ได้ใช้เธรดใด ๆ ระบบจะดำเนินการในเธรดเดียวกัน (เธรด GUI) หากคุณพูดถึงหัวข้อคุณสามารถดำเนินการTaskในหัวข้ออื่น นี่เป็นงานที่ไม่จำเป็นทำให้คุณสามารถเรียกใช้ผู้รับมอบสิทธิ์โดยตรงหรือแนบผู้รับมอบสิทธิ์นั้นไปยังเธรดและดำเนินการผู้รับมอบสิทธิ์นั้นในเธรดนั้น ดังนั้นอย่าใช้มัน มันไม่จำเป็นเลย หากคุณต้องการเพิ่มประสิทธิภาพซอฟต์แวร์ของคุณนี่เป็นตัวเลือกที่ดีที่จะลบออก

** โปรดทราบว่าเป็นActiondelegate


6

นอกจากประเด็นข้างต้นแล้วยังเป็นการดีที่จะรู้ว่า:

  1. งานโดยค่าเริ่มต้นเป็นงานพื้นหลัง คุณไม่สามารถทำงานเบื้องหน้าได้ ในทางตรงกันข้ามด้ายสามารถเป็นพื้นหลังหรือเบื้องหน้า (ใช้คุณสมบัติ IsBackground เพื่อเปลี่ยนพฤติกรรม)
  2. งานที่สร้างในเธรดพูลรีไซเคิลเธรดซึ่งช่วยประหยัดทรัพยากร ดังนั้นในกรณีส่วนใหญ่งานควรเป็นตัวเลือกเริ่มต้นของคุณ
  3. หากการดำเนินการเป็นไปอย่างรวดเร็วจะดีกว่าการใช้งานแทนเธรด สำหรับการดำเนินการที่รันนานงานไม่ได้ให้ประโยชน์มากกว่าเธรด

4

ฉันมักจะใช้Taskเพื่อโต้ตอบกับ Winforms และผู้ทำงานเบื้องหลังที่เรียบง่ายเพื่อไม่ให้ค้าง UI นี่คือตัวอย่างเมื่อฉันต้องการใช้Task

private async void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    await Task.Run(() => {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
    })
    buttonDownload.Enabled = true;
}

VS

private void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    Thread t = new Thread(() =>
    {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
        this.Invoke((MethodInvoker)delegate()
        {
            buttonDownload.Enabled = true;
        });
    });
    t.IsBackground = true;
    t.Start();
}

ความแตกต่างคือคุณไม่จำเป็นต้องใช้MethodInvokerและรหัสที่สั้นกว่า


4

งานเป็นเหมือนการดำเนินการที่คุณต้องการดำเนินการเธรดช่วยจัดการการดำเนินการเหล่านั้นผ่านโหนดกระบวนการจำนวนมาก งานที่เป็นตัวเลือกที่มีน้ำหนักเบาเป็น Threading สามารถนำไปสู่การจัดการรหัสซับซ้อน
ผมจะขอแนะนำให้อ่านจาก MSDN (ที่ดีที่สุดในโลก) เสมอ

งาน

เกลียว


3

งานสามารถถูกมองว่าเป็นวิธีที่สะดวกและง่ายต่อการเรียกใช้งานบางอย่างแบบอะซิงโครนัสและแบบขนาน

โดยปกติแล้วงานคือสิ่งที่คุณต้องการฉันจำไม่ได้ว่าฉันเคยใช้เธรดเป็นอย่างอื่นมากกว่าการทดลองหรือไม่

คุณสามารถทำสิ่งเดียวกันให้สำเร็จโดยใช้เธรด (มีความพยายามมาก) เท่าที่จะทำได้กับงาน

เกลียว

int result = 0;
Thread thread = new System.Threading.Thread(() => { 
    result = 1; 
});
thread.Start();
thread.Join();
Console.WriteLine(result); //is 1

งาน

int result = await Task.Run(() => {
    return 1; 
});
Console.WriteLine(result); //is 1

โดยปกติแล้วงานจะใช้เธรดพูลซึ่งช่วยประหยัดทรัพยากรเนื่องจากการสร้างเธรดอาจมีราคาแพง คุณสามารถเห็นงานที่เป็นนามธรรมในระดับที่สูงขึ้นเมื่อกระทู้

ในฐานะที่เป็นบทความนี้ชี้ให้เห็นงานดังต่อไปนี้ให้คุณลักษณะที่มีประสิทธิภาพมากกว่าด้าย

  • งานได้รับการปรับแต่งเพื่อใช้ประโยชน์จากโปรเซสเซอร์มัลติคอร์

  • หากระบบมีหลายภารกิจระบบจะใช้ประโยชน์จากพูลเธรด CLR ภายในและดังนั้นจึงไม่มีโอเวอร์เฮดที่เกี่ยวข้องกับการสร้างเธรดเฉพาะโดยใช้เธรด ยังลดเวลาการสลับบริบทระหว่างหลายเธรด

  • ภารกิจสามารถส่งคืนผลลัพธ์ไม่มีกลไกโดยตรงที่จะส่งคืนผลลัพธ์จากเธรด
  • รอชุดของงานโดยไม่ต้องสร้างสัญญาณ

  • เราสามารถเชื่อมโยงงานต่างๆเข้าด้วยกันเพื่อดำเนินการอย่างต่อเนื่อง

  • สร้างความสัมพันธ์ระหว่างผู้ปกครอง / เด็กเมื่อเริ่มงานหนึ่งจากงานอื่น

  • ข้อยกเว้นภารกิจลูกสามารถเผยแพร่ไปยังภารกิจหลัก

  • การยกเลิกการสนับสนุนงานผ่านการใช้โทเค็นการยกเลิก

  • การใช้งานแบบอะซิงโครนัสนั้นง่ายในการใช้งานโดยใช้ 'async' และ 'คอย'

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.