หลายเธรดสามารถทำสิ่งใดที่เธรดเดี่ยวไม่สามารถทำได้? [ปิด]


100

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


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

11
แน่นอนถ้าคุณมีหลายคอร์โปรเซสเซอร์ในคอมพิวเตอร์ของคุณ (ซึ่งเป็นเรื่องธรรมดาในปัจจุบัน) โปรแกรมของคุณสามารถใช้เธรดเพื่อใช้ประโยชน์จากคอร์เพิ่มเติมเพื่อดำเนินการประมวลผลของคุณและคุณจะเห็นการเร่งความเร็วเพราะคุณขว้างแรงม้ามากขึ้น ปัญหาการใช้คอมพิวเตอร์ของคุณ หากคุณไม่ได้ใช้เธรดเลยโปรแกรมของคุณจะใช้หน่วยประมวลผลกลางเดียวซึ่งก็ใช้ได้ถ้าคุณต้องการ
Robert Harvey

14
โปรแกรมแบบเธรดเดียวไม่สามารถหยุดชะงักทรัพยากรในวิธีที่โปรแกรมแบบมัลติเธรดสามารถ
zzzzBov

3
คำตอบ: ทำงานบนสองคอร์
Daniel Little

6
คำตอบ: ทำให้เกิดการหยุดชะงัก
งาน

คำตอบ:


111

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

คำตอบง่ายๆคือใช่ คุณสามารถเขียนโค้ดใด ๆ เพื่อให้ทำงานบนเธรดเดี่ยว หลักฐาน: ระบบตัวประมวลผลเดียวอาจเรียกใช้คำแนะนำเชิงเส้นเท่านั้น การมีการประมวลผลหลายบรรทัดทำได้โดยการขัดจังหวะการประมวลผลระบบปฏิบัติการบันทึกสถานะของเธรดปัจจุบันและเริ่มต้นการทำงานใหม่

ซับซ้อนคำตอบคือ ... ที่ซับซ้อนมากขึ้น! เหตุผลที่โปรแกรมแบบมัลติเธรดอาจมีประสิทธิภาพมากกว่าโปรแกรมเชิงเส้นเนื่องจากฮาร์ดแวร์ "ปัญหา" CPU สามารถประมวลผลการคำนวณได้เร็วกว่าหน่วยความจำและที่เก็บข้อมูลอย่างหนัก IO ตัวอย่างเช่นคำสั่ง "เพิ่ม" ดำเนินการได้เร็วกว่า "ดึงข้อมูล" แคชและคำสั่งโปรแกรมเฉพาะที่ดึงมา (ไม่แน่ใจในคำศัพท์ที่แน่นอนที่นี่) สามารถต่อสู้เรื่องนี้ได้บ้าง แต่ปัญหาเรื่องความเร็วยังคงอยู่

การทำเกลียวเป็นวิธีในการต่อสู้กับความไม่ตรงกันนี้โดยใช้ CPU สำหรับคำสั่งที่ผูกกับ CPU ในขณะที่คำสั่งของ IO นั้นเสร็จสิ้นแล้ว แผนการดำเนินการเธรดโดยทั่วไปน่าจะเป็น: ดึงข้อมูลประมวลผลข้อมูลเขียนข้อมูล สมมติว่าการดึงข้อมูลและการเขียนใช้เวลา 3 รอบและการประมวลผลใช้เวลาเพียงครั้งเดียวเพื่อวัตถุประสงค์ในการอธิบาย คุณสามารถเห็นได้ว่าในขณะที่คอมพิวเตอร์กำลังอ่านหรือเขียนมันไม่ได้ทำอะไรเลย 2 รอบเลยละ? เห็นได้ชัดว่ามันขี้เกียจและเราจำเป็นต้องทุบแส้การเพิ่มประสิทธิภาพของเรา!

เราสามารถเขียนกระบวนการโดยใช้เธรดเพื่อใช้เวลาที่สูญเปล่านี้:

  1. อันดับที่ 1
  2. ไม่มีการดำเนินการ
  3. # 2 fetch
  4. # 1 ดำเนินการเสร็จแล้ว
  5. เขียน # 1
  6. อันดับที่ 1
  7. # 2 เสร็จแล้วประมวลผล
  8. เขียน # 2
  9. เรียก # 2

และอื่น ๆ เห็นได้ชัดว่านี่เป็นตัวอย่างที่มีการวางแผนมาบ้าง แต่คุณสามารถดูได้ว่าเทคนิคนี้สามารถใช้เวลาที่ต้องใช้เพื่อรอ IO ได้อย่างไร

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

นั่นคือทั้งหมดที่ดีและดีสำหรับหน่วยประมวลผลหลักเดียว แต่โปรเซสเซอร์ที่ทันสมัยส่วนใหญ่มีสองแกนหรือมากกว่า เธรดยังคงมีวัตถุประสงค์เดียวกัน - เพื่อเพิ่มการใช้งาน CPU ให้สูงสุด แต่คราวนี้เรามีความสามารถในการเรียกใช้สองคำสั่งแยกกันในเวลาเดียวกัน สิ่งนี้สามารถลดเวลาการทำงานลงได้ด้วยปัจจัยที่มีหลายคอร์ให้ใช้งานเพราะคอมพิวเตอร์กำลังทำงานหลายอย่างพร้อมกันไม่ใช่การสลับบริบท

ด้วยหลายคอร์เธรดจัดเตรียมวิธีการแบ่งงานระหว่างสองคอร์ ข้างต้นยังคงใช้สำหรับแต่ละแกนหลักแม้ว่า; โปรแกรมที่รันประสิทธิภาพสูงสุดด้วยสองเธรดบนหนึ่งคอร์ส่วนใหญ่จะทำงานที่ประสิทธิภาพสูงสุดโดยมีเธรดประมาณสี่เธรดบนสองคอร์ (วัดประสิทธิภาพได้ที่นี่ด้วยการประมวลผลคำสั่ง NOP ขั้นต่ำ)

ปัญหาเกี่ยวกับการรันเธรดบนหลายคอร์ (เมื่อเทียบกับคอร์แกนเดี่ยว) โดยทั่วไปจะได้รับการดูแลโดยฮาร์ดแวร์ ซีพียูจะต้องแน่ใจว่าล็อคตำแหน่งหน่วยความจำที่เหมาะสมก่อนที่จะอ่าน / เขียน (ฉันอ่านแล้วว่าใช้หน่วยความจำแฟลกบิตพิเศษสำหรับสิ่งนี้ แต่สามารถทำได้หลายวิธี) ในฐานะโปรแกรมเมอร์ที่มีภาษาระดับสูงกว่าคุณไม่ต้องกังวลอะไรอีกแล้วในสองคอร์ในขณะที่คุณ จะต้องมีด้วย

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


17
คำตอบที่ยอดเยี่ยม ฉันแค่อยากจะอธิบายอย่างละเอียดว่าหลังจากผ่านไประยะหนึ่งแล้วการเพิ่มเธรดเพิ่มเติมสามารถทำให้โปรแกรมทำงานช้าลงเพราะใช้เวลา "สูญเปล่า" ไปก่อนหน้านี้ทั้งหมดและตอนนี้คุณแค่เพิ่มการสลับบริบทเพิ่มเติม
Karl Bielefeldt

8
+1 สำหรับการอธิบายว่าเธรดช่วยในการบล็อกการประมวลผลของ IO อย่างไร การใช้เธรดอื่นคือการอนุญาตให้ UI ประมวลผลการกระทำของผู้ใช้ (การเคลื่อนไหวของเมาส์, แถบความคืบหน้า, การกดแป้นพิมพ์) เพื่อให้การรับรู้ของผู้ใช้คือโปรแกรมนั้นไม่ 'หยุด'
jqa

3
นอกจากนี้อีกหนึ่งอย่าง : การพูดอย่างเคร่งครัดเธรดไม่เพิ่มความหมายใด ๆ ให้กับภาษาส่วนใหญ่ คุณสามารถใช้มัลติเพล็กซิ่งทุกอย่างด้วยตัวคุณเองเพื่อให้ได้ผลลัพธ์เดียวกัน ในทางปฏิบัติแม้ว่าสิ่งนี้จะเป็นจำนวนเงินในการปรับใช้ทั้งเธรดไลบรารีของคุณใหม่ - เช่นเดียวกับการเรียกซ้ำไม่จำเป็นอย่างเคร่งครัด แต่สามารถเลียนแบบได้ด้วยการเขียน call stack ของคุณเอง
Kilian Foth

9
@james: ฉันต้องการจำแนกการประมวลผล UI เป็นเพียงรูปแบบอื่นของ I / O อาจแย่ที่สุดเนื่องจากผู้ใช้มีแนวโน้มที่จะล้าหลังและไม่เป็นเชิงเส้น :)
TMN

7
คำถามและคำตอบรอบ ๆ คอมพิวเตอร์นั้นเงียบงันเป็นคอร์หรือซีพียู นาฬิกาของฉันมีสองคอร์ฉันสงสัยว่าสมมติฐานนั้นใช้ได้กับเครื่องที่ทันสมัย
blueberryfields

37

หลายเธรดสามารถทำสิ่งใดที่เธรดเดี่ยวไม่สามารถทำได้?

ไม่มีอะไร

ร่างหลักฐานง่าย ๆ :

  • [การคาดเดาของโบสถ์ - ทัวริง] ⇒ทุกสิ่งที่สามารถคำนวณได้สามารถคำนวณได้โดย Universal Turing Machine
  • เครื่องจักรทัวริงสากลเป็นเธรดเดียว
  • ดังนั้นทุกอย่างที่สามารถคำนวณได้สามารถคำนวณได้ด้วยเธรดเดี่ยว

อย่างไรก็ตามโปรดทราบว่ามีข้อสันนิษฐานขนาดใหญ่ซ่อนอยู่ในนั้นนั่นคือภาษาที่ใช้ภายในเธรดเดียวคือทัวริงสมบูรณ์

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

มาฟังภาษาฟังก์ชั่นรวมกัน [สำหรับผู้ที่ไม่คุ้นเคย: เหมือนกับการเขียนโปรแกรมฟังก์ชั่นคือการเขียนโปรแกรมด้วยฟังก์ชั่นการเขียนโปรแกรมฟังก์ชั่นรวมคือการเขียนโปรแกรมด้วยฟังก์ชั่นรวม]

ภาษาที่ใช้งานได้ทั้งหมดไม่ชัดเจนทัวริง: คุณไม่สามารถเขียนวนวนไม่สิ้นสุดใน TFPL (อันที่จริงคำจำกัดความของ "รวม") แต่คุณสามารถทำได้ในโปรแกรมทัวริงดังนั้นอย่างน้อยหนึ่งโปรแกรมที่ ไม่สามารถเขียนใน TFPL แต่สามารถเป็น UTM ได้ดังนั้น TFPL จึงมีประสิทธิภาพในการคำนวณน้อยกว่า UTM

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

ฉันคิดว่าภาษานี้จะทัวริงสมบูรณ์

อย่างน้อยที่สุดก็ตอบคำถามต้นกำเนิด:

หลายเธรดสามารถทำสิ่งใดที่เธรดเดี่ยวไม่สามารถทำได้?

หากคุณมีภาษาที่ไม่สามารถทำลูปอนันต์, แล้วแบบมัลติเธรดช่วยให้คุณทำลูปไม่มีที่สิ้นสุด

หมายเหตุแน่นอนว่าการวางไข่เธรดเป็นผลข้างเคียงและทำให้ภาษาที่ขยายของเราไม่เพียง แต่ไม่รวมอีกต่อไป แต่ยังไม่สามารถใช้งานได้อีกต่อไป


13
เมื่อคุณแสดงหลักฐาน "" ฉันไม่สามารถป้องกันตัวเองจาก "ดีจริง ๆ ... " คุณ ฉันคิดว่าคุณใช้ความคิดในทางที่ผิด หลักฐานเป็นสิ่งที่น่ารักและฉันได้รับคะแนนของคุณ แต่มันไม่ได้อยู่ในระดับที่เป็นนามธรรมที่เหมาะสม การไม่สามารถทำสิ่งต่าง ๆไม่เหมือนกับการคำนวณเช่นคุณไม่สามารถใช้หลักฐานเพื่อพูดได้เพราะเครื่องทัวริงสามารถคำนวณ "ทุกฟังก์ชันที่คำนวณได้" สามารถทำได้ภายใน 3 วินาที พูดอย่างเคร่งครัดเครื่อง threaded เดียวโดยไม่ขัดจังหวะไม่สามารถเข้าถึง I / O ในขณะที่หน่วยประมวลผลไม่ว่างทำการคำนวณ
xmm0

1
และไม่มีคอมพิวเตอร์เครื่องใดเป็นเครื่องทัวริง
เจนส์

1
@ColeJohnson: การหยุดชะงักเป็นรายละเอียดการใช้งาน เอาต์พุตที่มองเห็นคือ "ไม่หยุด" ซึ่งสามารถทำได้อย่างง่ายดายด้วยเธรดเดี่ยว
Heinzi

1
@ Heinzi: "ทำได้ง่าย" เป็นคำพูด ถ้าฉันจำได้ถูกต้องโปรแกรมที่สองหรือสามที่ฉันเคยเขียนก็ทำอย่างนั้น! ;-)
Joachim Sauer

1
ถ้าจักรวาลเป็นเธรดเดี่ยวเอ็ดแล้วทฤษฎีสตริงคืออะไร? คุณไม่สามารถโต้เถียงกับวิทยาศาสตร์แบบนี้
corsiKa

22

ในทางทฤษฎีทุกสิ่งที่โปรแกรมแบบมัลติเธรดสามารถทำได้ด้วยโปรแกรมแบบเธรดเดียวเช่นกันเพียงช้าลง

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

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


สกรีนเซฟเวอร์ SETI เป็นตัวอย่างของการเธรดในแง่ที่ว่ามันทำงานบนเธรดพื้นหลังในคอมพิวเตอร์ของคุณ; คุณยังคงสามารถทำสิ่งต่าง ๆ บนคอมพิวเตอร์ของคุณในขณะที่มันกำลังบีบข้อมูลในเบื้องหลัง หากไม่มีเธรดจะมีการหยุดชั่วคราวในขณะที่สกรีนเซฟเวอร์ SETI ทำการคำนวณ คุณจะต้องรอให้เสร็จก่อนที่คุณจะสามารถทำงานต่อไปได้
Robert Harvey

3
@Robert: ฉันคิดว่าPéterใช้ SETI @ Home เพื่ออธิบายแนวคิดของการแยกงานเพื่อใช้กับโปรเซสเซอร์หลายตัว การคำนวณแบบกระจายนั้นแตกต่างจากมัลติเธรด แต่แนวคิดก็คล้ายคลึงกัน
Steven

5
ฉันคิดว่าตัวอย่าง SETI นั้นเป็นการคำนวณแบบกระจายแทนที่จะเป็นแบบหลายเธรด แนวคิดที่คล้ายกัน แต่ไม่เหมือนกัน

11

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

- ปัญหาเกี่ยวกับเธรด (www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf)

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

ข้อเสียอย่างหนึ่งของการใช้เธรดที่ไม่ได้กล่าวถึงที่นี่คือการสูญเสียการแบ่งส่วนทรัพยากรที่คุณได้รับด้วยช่องว่างกระบวนการเธรดเดี่ยว ตัวอย่างเช่นสมมติว่าคุณพบกรณีของ segfault ในบางกรณีมีความเป็นไปได้ที่จะกู้คืนจากสิ่งนี้ในแอพพลิเคชั่นแบบหลายขั้นตอนโดยที่คุณสามารถปล่อยให้เด็กที่มีความผิดพลาดตายและเกิดใหม่ได้ นี่เป็นกรณีในแบ็กเอนด์ prefork ของ Apache เมื่ออินสแตนซ์ httpd หนึ่งรายการหยุดทำงานกรณีที่แย่กว่านั้นคือคำขอ HTTP เฉพาะอาจถูกดร็อปสำหรับกระบวนการนั้น แต่ Apache จะวางลูกใหม่และมักจะร้องขอหากเพียงแค่ส่งใหม่และรับบริการ ผลลัพธ์ที่ได้คือ Apache โดยรวมไม่ได้ถูกลบออกด้วยเธรดที่ผิดพลาด

สิ่งที่ต้องพิจารณาในสถานการณ์นี้คือหน่วยความจำรั่ว มีบางกรณีที่คุณสามารถจัดการเธรดที่ล้มเหลวได้อย่างงดงาม (บน UNIX, การกู้คืนจากสัญญาณบางอย่าง - แม้ segfault / fpviolation - เป็นไปได้) แต่ในกรณีนั้นคุณอาจรั่วหน่วยความจำทั้งหมดที่จัดสรรโดยเธรดนั้น (malloc, new, etc. ) ดังนั้นในขณะที่กระบวนการของคุณอาจเปิดอยู่มันจะรั่วไหลหน่วยความจำมากขึ้นเรื่อย ๆ เมื่อเกิดข้อผิดพลาด / การกู้คืนแต่ละครั้ง อีกครั้งมีวิธีการบางอย่างเพื่อลดขนาดนี้เช่นการใช้พูลหน่วยความจำของ Apache แต่นี่ยังไม่ป้องกันหน่วยความจำที่อาจถูกจัดสรรโดยบุคคลที่สาม libs ที่อาจใช้เธรด

และในขณะที่บางคนชี้ให้เห็นการทำความเข้าใจกับการซิงโครไนซ์ดั้งเดิมอาจเป็นสิ่งที่ยากที่สุดที่จะทำให้ถูกต้อง ปัญหานี้ด้วยตัวเอง - แค่รับตรรกะทั่วไปที่ถูกต้องสำหรับรหัสของคุณทั้งหมด - อาจปวดหัวมาก การหยุดชะงักลึกลับมักเกิดขึ้นในช่วงเวลาที่แปลกประหลาดและบางครั้งก็ไม่ได้จนกว่าโปรแกรมของคุณจะทำงานในการผลิตซึ่งทำให้การดีบักยากขึ้นทั้งหมด เพิ่มความจริงที่ว่าการซิงโครไนซ์แบบดั้งเดิมมักจะแตกต่างกันอย่างมากกับแพลตฟอร์ม (Windows กับ POSIX) และการดีบักอาจเป็นเรื่องยากมากขึ้นรวมถึงความเป็นไปได้สำหรับสภาพการแข่งขันในเวลาใดก็ได้ (เริ่มต้น / เริ่มต้นเริ่มต้นใช้งานและปิด) การเขียนโปรแกรมด้วยเธรดมีความเมตตาเล็กน้อยสำหรับผู้เริ่มต้น และแม้กระทั่งสำหรับผู้เชี่ยวชาญ ยังคงมีความเมตตาเพียงเล็กน้อยเพราะความรู้ในการทำเกลียวตัวเองไม่ได้ลดความซับซ้อนลงโดยทั่วไป แต่ละครั้งของโค้ดเธรดแต่ละครั้งดูเหมือนจะเพิ่มความซับซ้อนโดยรวมของโปรแกรมเป็นอย่างมากรวมทั้งเพิ่มความน่าจะเป็นสำหรับการหยุดชะงักที่ซ่อนอยู่หรือสภาพการแข่งขันที่แปลกไปยังพื้นผิวได้ตลอดเวลา มันอาจเป็นเรื่องยากมากที่จะเขียนกรณีทดสอบเพื่อค้นหาสิ่งเหล่านี้

นี่คือเหตุผลที่บางโครงการเช่น Apache และ PostgreSQL นั้นใช้กระบวนการเป็นส่วนใหญ่ PostgreSQL รันทุก ๆ แบ็กเอนด์เธรดในกระบวนการแยกกัน แน่นอนว่านี่ยังไม่ได้ช่วยบรรเทาปัญหาการซิงโครไนซ์และเงื่อนไขการแข่งขัน แต่มันเพิ่มการป้องกันเล็กน้อยและในบางวิธีทำให้สิ่งต่าง ๆ ง่ายขึ้น

กระบวนการหลายกระบวนการแต่ละการรันการประมวลผลเธรดเดียวสามารถทำได้ดีกว่าการประมวลผลหลายเธรดในกระบวนการเดียว และด้วยการมาถึงของรหัสเพียร์ทูเพียร์ใหม่เช่น AMQP (RabbitMQ, Qpid ฯลฯ ) และ ZeroMQ มันง่ายกว่ามากในการแยกเธรดในพื้นที่กระบวนการที่แตกต่างกันและแม้แต่เครื่องจักรและเครือข่ายทำให้สิ่งต่าง ๆ ง่ายขึ้นอย่างมาก แต่ถึงกระนั้นก็ไม่ใช่กระสุนเงิน ยังคงมีความซับซ้อนในการจัดการกับ คุณเพียงแค่ย้ายตัวแปรบางตัวจากพื้นที่กระบวนการไปยังเครือข่าย

บรรทัดล่างคือการตัดสินใจที่จะเข้าสู่โดเมนของกระทู้ไม่เบา เมื่อคุณก้าวเข้าสู่ดินแดนนั้นแทบจะในทันทีทันใดทุกอย่างก็จะซับซ้อนมากขึ้นและปัญหาใหม่ ๆ เข้ามาในชีวิตของคุณ มันอาจสนุกและเท่ห์ แต่ก็เหมือนพลังงานนิวเคลียร์ - เมื่อสิ่งต่าง ๆ ผิดพลาดพวกเขาสามารถไปได้ไม่ดีและรวดเร็ว ฉันจำได้ว่าได้เข้าเรียนในการฝึกอบรมเรื่องวิกฤติเมื่อหลายปีก่อนและพวกเขาแสดงรูปภาพของนักวิทยาศาสตร์บางคนที่ลอสอาลามอสซึ่งเล่นกับพลูโทเนียมในห้องทดลองในสงครามโลกครั้งที่สอง หลายคนใช้มาตรการป้องกันเล็กน้อยหรือไม่มีเลยกับเหตุการณ์ที่มีการเปิดรับแสงและในพริบตา - ในแฟลชที่สว่างไสวและไม่เจ็บปวดเพียงครั้งเดียว วันต่อมาพวกเขาก็ตาย Richard Feynman ต่อมาเรียกสิ่งนี้ว่า " กระตุ้นหางของมังกร(อย่างน้อยก็สำหรับฉันแล้ว) ดูเหมือนว่าจะไม่มีพิษภัยในตอนแรกและเมื่อถึงเวลาที่คุณกัดคุณก็จะเกาหัวของคุณในสิ่งที่เกิดขึ้นอย่างรวดเร็ว แต่อย่างน้อยก็ชนะ ไม่ต้องฆ่าคุณ


TL ของฉัน; DR ตอบสนองซีดเมื่อเทียบกับส่วนที่ดีนี้ในสิ่งที่หัวข้อเดียวจะไม่ทำเพื่อคุณ
bmike

1
พักดื่มกาแฟยามบ่าย
Mike Owens

10

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

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

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

ข้อเสียของการพัฒนาแบบมัลติเธรดคือคุณต้องเข้าใจเกี่ยวกับปัญหาการเกิดพร้อมกันกลยุทธ์การล็อคและอื่น ๆ การพัฒนาโค้ดหลายเธรดที่ปราศจากข้อผิดพลาดนั้นค่อนข้างยาก และการแก้ไขข้อบกพร่องอาจยิ่งยากขึ้น


9

มีบางสิ่งที่มีอยู่ที่สามารถทำได้โดยใช้หลายเธรดเท่านั้น?

ใช่. คุณไม่สามารถเรียกใช้รหัสบน CPU หลายแกนหรือแกนประมวลผล CPU ด้วยเธรดเดียว

หากไม่มีหลาย CPU / แกนประมวลผลเธรดยังสามารถทำให้โค้ดที่ทำงานแบบขนานนั้นง่ายขึ้นเช่นการจัดการไคลเอ็นต์บนเซิร์ฟเวอร์ - แต่คุณสามารถทำสิ่งเดียวกันได้โดยไม่ต้องใช้เธรด


6

หัวข้อไม่เพียง แต่เกี่ยวกับความเร็ว แต่เกี่ยวกับการทำงานพร้อมกัน

หากคุณไม่มีแบทช์แอปพลิเคชันตามที่ @Peter แนะนำ แต่แทนที่จะเป็นชุดเครื่องมือ GUI เช่น WPF คุณจะโต้ตอบกับผู้ใช้และตรรกะทางธุรกิจด้วยเธรดเดียวได้อย่างไร

นอกจากนี้สมมติว่าคุณกำลังสร้างเว็บเซิร์ฟเวอร์ คุณจะให้บริการผู้ใช้มากกว่าหนึ่งคนพร้อมกันเพียงหนึ่งเธรดได้อย่างไร (หากไม่มีกระบวนการอื่น)

มีหลายสถานการณ์ที่เพียงแค่เธรดเดียวยังไม่เพียงพอ นั่นเป็นเหตุผลที่ความก้าวหน้าล่าสุดเช่นโปรเซสเซอร์ Intel MIC ที่มีมากกว่า 50 คอร์และเธรดหลายร้อยรายการเกิดขึ้น

ใช่การเขียนโปรแกรมแบบขนานและพร้อมกันนั้นยาก แต่จำเป็น


3
"ด้ายเพียงเส้นเดียว" อาจหมายถึงหลายสิ่ง - คุณสามารถใช้การเชื่อมต่อแบบมีตัวคั่นเพื่อจำลองการทำงานหลายอย่างแบบมีส่วนร่วมภายในเธรดเดียว (OS หรือสีเขียว)
Frank Shearar

แน่นอน แต่ฉันให้บริบทเล็กน้อยกับคำถาม @ Frank +1 สำหรับคำแนะนำ
Randolf Rincón Fadul

3
เธรดอาจทำให้ง่ายขึ้น แต่งานทั้งหมดที่คุณกล่าวถึงสามารถทำได้โดยไม่มีเธรดและเคยเป็น ตัวอย่างเช่นวง GUI canonical เคยเป็นในขณะที่ (จริง) {เหตุการณ์ GUI กระบวนการ; คำนวณบิต} ไม่จำเป็นต้องมีเธรด
KeithB

มี webservers แบบเธรดเดี่ยวกระบวนการเดียว lighttpd ตัวอย่างเช่นหรือ thttpd หรือ nginx (แม้ว่าหลังสามารถเรียกใช้มากกว่าหนึ่งกระบวนการ แต่ค่าเริ่มต้นคือหนึ่งมันยังเป็นเธรดเดียวเสมอ) พวกเขาไม่มีปัญหาในการแสดงมากกว่าหนึ่งผู้ใช้
StasM

6

Multi-Threading สามารถให้อินเตอร์เฟส GUI ยังคงตอบสนองได้ในระหว่างการดำเนินการที่ยาวนาน หากไม่มีหลายเธรดผู้ใช้จะต้องดูรูปแบบที่ถูกล็อคไว้ในขณะที่กระบวนการที่ยาวกำลังทำงานอยู่


ไม่ถูกต้องทั้งหมด ในทางทฤษฎีคุณสามารถแบ่งการดำเนินงานที่ยาวนานของคุณออกเป็นหน่วยเล็ก ๆ และอัพเดตการประมวลผลเหตุการณ์ระหว่างนั้นได้
Sebastian Negraszus

@ Sebastion N. แต่ถ้ากระบวนการที่ยาวนานไม่สามารถปรับสภาพให้เล็กลงได้ล่ะ? ความสามารถในการเรียกใช้กระบวนการในเธรดอื่นสามารถเพิ่มเธรด GUI (เธรดหลัก) เพื่อให้สามารถตอบสนองได้
LarsTech

5

รหัสแบบมัลติเธรดสามารถหยุดการทำงานของลอจิกโปรแกรมและเข้าถึงข้อมูลเก่าในลักษณะที่เธรดเดี่ยวไม่สามารถทำได้

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


4

แอพที่เกี่ยวข้องกับการบล็อก IO ที่ยังต้องตอบสนองต่ออินพุตอื่น ๆ (GUI หรือการเชื่อมต่ออื่น ๆ ) ไม่สามารถทำได้แบบเธรดเดียว

การเพิ่มวิธีการตรวจสอบใน IO lib เพื่อดูว่าสามารถอ่านได้มากน้อยเพียงใดโดยไม่มีการปิดกั้นสามารถช่วยได้ แต่มีไลบรารีไม่กี่แห่งที่รับประกันเรื่องนี้อย่างเต็มที่


พวกเขาสามารถเธรดเดียว (และมักจะ) รหัสบอกให้เคอร์เนลเริ่มต้น IO และรับสัญญาณเมื่อมันเสร็จสมบูรณ์ ในขณะที่รอสัญญาณมันสามารถทำการประมวลผลอื่น ๆ
KeithB

@keith นั่นไม่ใช่การปิดกั้น IO ฉันพูดโดยเฉพาะการบล็อก
เฟือง

แต่ทำไมคุณถึงปิดกั้น IO หากคุณต้องการตอบสนอง มีการบล็อก IO ที่โดยทั่วไปแล้วไม่มีทางเลือกที่ไม่ใช่การปิดกั้น? ฉันไม่สามารถคิดอะไรได้
KeithB

@keith java ของ RMI กำลังบล็อกและในขณะที่คุณสามารถใช้การเรียกกลับ (แต่มันสร้างเธรดอื่น) ด้วย แต่สามารถถูกบล็อกโดยไฟร์วอลล์
ratchet freak

ฟังดูเหมือนข้อ จำกัด ของ Java RMI ไม่ใช่ข้อ จำกัด โดยธรรมชาติของรหัสเธรดเดี่ยว นอกจากนี้หากคุณกำลังทำ RMI คุณมี "การดำเนินการ" เธรดที่แยกต่างหากเพียงแค่บนเครื่องระยะไกล
KeithB

4

มีคำตอบที่ดีมากมาย แต่ฉันไม่แน่ใจว่าวลีใดเป็นอย่างที่ฉันต้องการ - บางทีนี่อาจเป็นวิธีที่แตกต่างในการดู:

เธรดเป็นเพียงการทำให้โปรแกรมง่ายขึ้นเช่น Objects หรือ Actors หรือสำหรับลูป (ใช่สิ่งที่คุณใช้กับลูปที่คุณสามารถนำไปใช้กับ if / goto)

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

พวกเขายังทำให้ภาษาง่ายขึ้นสำหรับการแบ่งการดำเนินการรันไทม์ลงในกลุ่มที่เป็นมิตรกับ CPU หลายตัว (ฉันเชื่อว่านักแสดง)

Java มีเธรด "สีเขียว" บนระบบที่ระบบปฏิบัติการไม่สนับสนุนการเธรดใด ๆ ในกรณีนี้มันง่ายกว่าที่จะเห็นว่าไม่มีอะไรมากไปกว่าการเขียนโปรแกรมเชิงนามธรรม


+1 - เธรดเป็นเพียงสิ่งที่เป็นนามธรรมสำหรับการขนานในเครื่องที่มีลำดับขั้นพื้นฐานเช่นเดียวกับภาษาการเขียนโปรแกรมระดับสูงที่ให้รหัสของเครื่องที่เป็นนามธรรม
mouviciel

0

ระบบปฏิบัติการใช้แนวคิดการแบ่งส่วนเวลาที่แต่ละเธรดได้รับถึงเวลาที่ต้องรันจากนั้นถูกยึดเอาไว้ วิธีการเช่นนี้สามารถแทนที่เธรดได้เหมือนตอนนี้ แต่การเขียนตัวกำหนดตารางเวลาของคุณเองในทุกแอปพลิเคชันจะมีค่าใช้จ่ายมากเกินไป ยิ่งกว่านั้นคุณต้องทำงานกับอุปกรณ์ I / O และอื่น ๆ และต้องการการสนับสนุนจากด้านฮาร์ดแวร์เพื่อให้คุณสามารถเริ่มการขัดจังหวะเพื่อเรียกใช้ตัวกำหนดตารางเวลาของคุณ โดยทั่วไปคุณจะต้องเขียนระบบปฏิบัติการใหม่ทุกครั้ง

โดยทั่วไปเธรดสามารถปรับปรุงประสิทธิภาพในกรณีที่เธรดรอ I / O หรือกำลังหลับ นอกจากนี้ยังช่วยให้คุณสร้างอินเทอร์เฟซที่ตอบสนองและอนุญาตให้กระบวนการหยุดในขณะที่คุณทำงานที่ยาวนาน และการทำเกลียวยังช่วยปรับปรุงสิ่งต่างๆบน CPU ที่มีมัลติคอร์ที่แท้จริง


0

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

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

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

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

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

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