มัลติเธรด: อะไรคือจุดของเธรดที่มากกว่าคอร์?


142

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


52
เราสนุกกับคำถามประเภทนี้พวกเขาถามถึงพื้นฐานของบางสิ่งบางอย่างที่ได้รับอนุญาต .. ให้มา .. ..
Srinivas Reddy Thatiparthy

6
ครั้งสุดท้ายที่คุณมี Firefox, MS Word, Winamp, Eclipse และตัวจัดการการดาวน์โหลด (มากกว่าสี่โปรแกรม / กระบวนการ) ทำงานพร้อมกันบนเครื่อง quad core ของคุณคืออะไร? นอกจากนี้บางครั้งแอปพลิเคชันหนึ่งอาจวางไข่มากกว่าสี่เธรด
Amarghosh

1
การขโมยไม่จำเป็นต้องเลวร้าย คุณอาจมีเธรดที่มีลำดับความสำคัญสูงกว่าสำหรับงานสำคัญที่ต้องขโมยเวลา
kichik

1
@Amarghosh ฉันเดาว่าเป็นคำถามทำไมแอปพลิเคชันเดียวอาจต้องการวางไข่เธรดมากกว่าคอร์หากดูเหมือนจะไม่ก่อให้เกิดประโยชน์ด้านประสิทธิภาพใด ๆ และตัวอย่างของคุณที่มีมากกว่าสี่โปรแกรมนั้นไม่เกี่ยวข้องกันมากนัก ตามที่คุณบันทึกไว้อย่างถูกต้องสิ่งเหล่านี้เป็นกระบวนการ ฟีเจอร์มัลติทาสกิ้ง OS (การทำมัลติเพล็กซ์กระบวนการ) มีส่วนเกี่ยวข้องกับเธรดภายในกระบวนการเพียงเล็กน้อย
Aleksandr Ivannikov

คำตอบ:


81

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

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

จริงๆแล้วมันซับซ้อนกว่านั้น:

  • ถ้าคุณมีงานห้าชิ้นที่ต้องทำพร้อมกันล่ะ มันสมเหตุสมผลกว่าที่จะเรียกใช้พวกมันทั้งหมดในครั้งเดียวแทนที่จะวิ่งสี่คนแล้ววิ่งห้าในภายหลัง

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

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

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


16
+1 สำหรับ "แต่เฉพาะในกรณีที่แต่ละกระทู้ต้องการ CPU 100%" นั่นคือสมมติฐานที่ฉันไม่ได้ตระหนักว่าฉันกำลังทำอยู่
Nick Heiner

1
คำตอบที่ยอดเยี่ยมสำหรับคำถามที่ยอดเยี่ยม ขอบคุณ!
Edgecase

53

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

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


11
พูดได้ดี. อาร์กิวเมนต์ 'หนึ่งเธรดต่อ CPU' ใช้กับรหัสที่ผูกกับ CPU เท่านั้น การเขียนโปรแกรมแบบอะซิงโครนัสเป็นอีกเหตุผลหนึ่งที่ใช้เธรด
Joshua Davis

26

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

ในแอพพลิเคชั่นที่มีความซับซ้อนปานกลางการใช้เธรดเดียวพยายามทำทุกอย่างอย่างรวดเร็วทำให้แฮชของ 'flow' ของโค้ดของคุณ เธรดเดี่ยวใช้เวลาส่วนใหญ่ในการทำโพลนี้ตรวจสอบว่าเรียกรูทีนตามเงื่อนไขตามที่ต้องการและมันก็ยากที่จะเห็นสิ่งใดนอกเหนือจาก morut of minutiae

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

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


1
นี่เป็นความคิดที่น่าสนใจ ... ฉันเคยได้ยินเสมอว่าการใช้มัลติเธรดเป็นแอพที่เพิ่มความซับซ้อน แต่สิ่งที่คุณพูดมีเหตุผล
Nick Heiner

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

มีวิธีการจัดโครงสร้างแอปพลิเคชันแบบเธรดเดี่ยวเพื่อให้โฟลว์การควบคุมมีความชัดเจนในระดับที่คุณเขียนโปรแกรม OTOH ถ้าคุณสามารถจัดโครงสร้างเธรดของคุณเพื่อให้พวกเขาส่งข้อความถึงกันเท่านั้น (แทนที่จะมีทรัพยากรที่ใช้ร่วมกัน) ดังนั้นมันจึงค่อนข้างง่ายที่จะรู้ว่าเกิดอะไรขึ้นและทำให้ทุกอย่างทำงานได้
Donal Fellows

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

15

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

พิจารณาเธรดที่ต้องการอ่านข้อมูลจากฮาร์ดไดรฟ์ ในปี 2014 แกนประมวลผลทั่วไปทำงานที่ 2.5 GHz และอาจดำเนินการ 4 คำสั่งต่อรอบ ด้วยรอบเวลา 0.4 ns โปรเซสเซอร์สามารถประมวลผล 10 คำสั่งต่อนาโนวินาที ด้วยฮาร์ดไดรฟ์แบบกลไกทั่วไปที่ใช้เวลาค้นหาประมาณ 10 มิลลิวินาทีโปรเซสเซอร์สามารถประมวลผลคำสั่งได้ 100 ล้านคำสั่งในเวลาที่อ่านค่าจากฮาร์ดไดรฟ์ อาจมีการปรับปรุงประสิทธิภาพอย่างมีนัยสำคัญกับฮาร์ดไดรฟ์ที่มีแคชขนาดเล็ก (บัฟเฟอร์ 4 MB) และไดรฟ์ไฮบริดที่มีพื้นที่เก็บข้อมูลไม่กี่ GB เนื่องจากเวลาแฝงข้อมูลสำหรับการอ่านหรืออ่านตามลำดับจากส่วนไฮบริด

แกนประมวลผลสามารถสลับไปมาระหว่างเธรด (ค่าใช้จ่ายสำหรับการหยุดชั่วคราวและดำเนินการต่อเธรดอยู่ที่ประมาณ 100 รอบนาฬิกา) ในขณะที่เธรดแรกรออินพุต latency สูง (มีราคาแพงกว่ารีจิสเตอร์ (1 นาฬิกา) และ RAM (5 นาโนวินาที)) disk I / O, การเข้าถึงเครือข่าย (เวลาแฝงของ 250ms), การอ่านข้อมูลจากซีดีหรือบัสช้าหรือการโทรฐานข้อมูล การมีเธรดมากกว่าคอร์หมายความว่าสามารถทำงานที่มีประโยชน์ได้ในขณะที่งานที่มีความหน่วงสูงได้รับการแก้ไข

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

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

นี่คือเหตุผลที่การทำนายสาขามีความสำคัญ หากคุณมีคำสั่ง if ที่ต้องการโหลดค่าจาก RAM แต่เนื้อความของคำสั่ง if และ else ใช้ค่าที่โหลดไว้ในรีจิสเตอร์โปรเซสเซอร์อาจประมวลผลหนึ่งหรือทั้งสองสาขาก่อนที่จะประเมินเงื่อนไข เมื่อเงื่อนไขส่งคืนตัวประมวลผลจะใช้ผลลัพธ์ของสาขาที่เกี่ยวข้องและยกเลิกอีกฝ่าย การทำงานที่ไร้ประโยชน์ที่นี่อาจจะดีกว่าการเปลี่ยนไปใช้เธรดอื่นซึ่งอาจนำไปสู่การฟาดฟัน

ในขณะที่เราย้ายออกจากตัวประมวลผลแบบ single-core ความเร็วสูงสัญญาณนาฬิกาไปยังตัวประมวลผลแบบ multi-core การออกแบบชิปได้เน้นที่การอัดแกนเพิ่มเติมต่อตายปรับปรุงการแบ่งปันทรัพยากรบนชิประหว่างแกนประมวลผลการทำนายสาขาที่ดีขึ้น และการตั้งเวลาเธรดที่ดีขึ้น


เดียวกันสามารถทำได้ด้วยเธรดเดียวและคิวแม้ว่า: \ มีประโยชน์จริง ๆ กับการมี 80 เธรดใน 2-4 คอร์มากกว่าเพียงแค่มี 2-4 คอร์ที่เพิ่งกินงานออกจากคิวทันทีที่มาถึง และพวกเขาไม่มีอะไรทำ?
Dmitry

8

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

ลองพิจารณากรณีของโปรแกรมจำลองเทอร์มินัลอย่างง่าย คุณต้องทำสิ่งต่อไปนี้:

  • คอยดูอักขระที่เข้ามาจากระบบรีโมตและแสดง
  • ดูสิ่งที่มาจากแป้นพิมพ์และส่งไปยังระบบระยะไกล

(ตัวจำลองเทอร์มินัลจริงทำอะไรได้มากกว่ารวมถึงการสะท้อนสิ่งที่คุณพิมพ์ลงบนจอแสดงผลเช่นกัน แต่เราจะผ่านสิ่งนั้นไปในตอนนี้)

ตอนนี้การวนลูปสำหรับการอ่านจากรีโมตนั้นเป็นเรื่องง่าย

while get-character-from-remote:
    print-to-screen character

การวนลูปสำหรับการตรวจสอบคีย์บอร์ดและการส่งนั้นง่ายมาก:

while get-character-from-keyboard:
    send-to-remote character

อย่างไรก็ตามปัญหาคือคุณต้องทำพร้อมกัน ตอนนี้โค้ดต้องมีลักษณะเช่นนี้มากขึ้นถ้าคุณไม่มีเกลียว:

loop:
    check-for-remote-character
    if remote-character-is-ready:
        print-to-screen character
    check-for-keyboard-entry
    if keyboard-is-ready:
        send-to-remote character

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

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

แล้วทำไมคุณไม่ใช้เกลียว?

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

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


6

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

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

ดูคำถามที่เกี่ยวข้องนี้ด้วย: การใช้งานจริงสำหรับเธรด


การจัดการ UI เป็นตัวอย่างคลาสสิกของภารกิจที่ผูกไว้กับ IO มันไม่ดีเลยที่จะมีซีพียูแกนเดียวที่ทำทั้งการประมวลผลและงาน IO
Donal Fellows

6

ฉันไม่เห็นด้วยอย่างยิ่งกับการยืนยันของ @ kyoryu ว่าตัวเลขในอุดมคติคือหนึ่งเธรดต่อ CPU

คิดแบบนี้: ทำไมเราถึงมีระบบปฏิบัติการหลายกระบวนการ? สำหรับประวัติคอมพิวเตอร์ส่วนใหญ่คอมพิวเตอร์เกือบทุกเครื่องมี CPU หนึ่งตัว แต่จากปี 1960 เป็นต้นไปคอมพิวเตอร์ "ของจริง" ทั้งหมดมีระบบปฏิบัติการหลายระบบ (หรือที่เรียกว่ามัลติทาสกิ้ง)

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

ให้กันข้อโต้แย้งว่า Windows รุ่นก่อน NT เป็นมัลติทาสกิ้งหรือไม่ ตั้งแต่นั้นมาทุกระบบปฏิบัติการจริงมีหลายงาน บางคนไม่เปิดเผยให้กับผู้ใช้ แต่มีอยู่แล้วทำสิ่งต่าง ๆ เช่นฟังวิทยุโทรศัพท์มือถือพูดคุยกับชิป GPS ยอมรับอินพุตเมาส์ ฯลฯ

เธรดเป็นเพียงงานที่มีประสิทธิภาพมากกว่านี้เล็กน้อย ไม่มีความแตกต่างพื้นฐานระหว่างงานกระบวนการและเธรด

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

ฉันจะยอมรับว่าด้วยภาษาเชิงโพรซีเดอร์ส่วนใหญ่, C, C ++, Java ฯลฯ การเขียนโค้ดที่ปลอดภัยของเธรดที่เหมาะสมนั้นเป็นงานจำนวนมาก ด้วยซีพียู 6 คอร์ในตลาดวันนี้และซีพียู 16 คอร์อยู่ไม่ไกลฉันคาดหวังว่าผู้คนจะย้ายออกจากภาษาเก่าเหล่านี้เนื่องจากมัลติเธรดเป็นข้อกำหนดที่สำคัญยิ่งขึ้น

ความไม่ลงรอยกันกับ @kyoryu เป็นเพียง IMHO ส่วนที่เหลือคือข้อเท็จจริง


5
หากคุณมีเธรดที่เชื่อมโยงโปรเซสเซอร์จำนวนมากดังนั้นหมายเลขที่เหมาะสมที่สุดคือหนึ่งต่อ CPU (หรืออาจน้อยกว่านั้นเพื่อปล่อยให้จัดการ I / O และ OS และสิ่งนั้นทั้งหมด) หากคุณมีเธรดที่ถูกผูกไว้กับ IOคุณสามารถสแต็กกับ CPU ได้ค่อนข้างมาก แอพที่แตกต่างกันมีการผสมผสานระหว่างตัวประมวลผลและงานที่ผูกกับ IO เป็นธรรมชาติโดยสิ้นเชิง แต่ทำไมคุณต้องระวังด้วยการประกาศที่เป็นสากล
Donal Fellows

1
แน่นอนความแตกต่างที่สำคัญที่สุดระหว่างเธรดและกระบวนการคือใน Windows ไม่มี fork () ดังนั้นการสร้างโพรเซสจึงมีราคาแพงมากนำไปสู่การใช้เธรดมากกว่า
ninjalj

ยกเว้นการพับโปรตีน SETI และอื่น ๆ ไม่มีงานของผู้ใช้ในทางปฏิบัติที่คำนวณได้นานมาก มีความต้องการรับข้อมูลจากผู้ใช้พูดคุยกับดิสก์พูดคุยกับ DBMS ฯลฯ ใช่ค่าใช้จ่ายของ fork () เป็นหนึ่งในหลาย ๆ สิ่งที่ Cutler สาปแช่ง NT กับคนอื่น ๆ ที่ DEC รู้
fishtoprecords

5

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

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


2
คุณกำลังเขียนซอฟต์แวร์สำหรับระบบปฏิบัติการที่รองรับเธรด แต่ไม่มีความสามารถในการทำ multiplexing io? ฉันคิดว่าเว็บเซิร์ฟเวอร์อาจเป็นตัวอย่างที่ไม่ดีเนื่องจากในกรณีนี้การทำ multiplex io นั้นจะมีประสิทธิภาพมากกว่าการวางไข่มากกว่าคอร์
Jason Coco

3

หลายเธรดจะนอนหลับกำลังรออินพุตผู้ใช้ I / O และเหตุการณ์อื่น ๆ


ได้อย่างแน่นอน เพียงใช้ตัวจัดการงานบน Windows หรือ TOP บนระบบปฏิบัติการจริงและดูว่างาน / กระบวนการจำนวนมากเท่าไร มัน 90% หรือมากกว่าเสมอ
fishtoprecords

2

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


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

2

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

กระบวนการเป็นอีกชื่อหนึ่งของแอปพลิเคชัน โดยทั่วไปกระบวนการเป็นอิสระจากกัน หากกระบวนการหนึ่งตายมันไม่ทำให้กระบวนการอื่นตายเช่นกัน เป็นไปได้สำหรับกระบวนการในการสื่อสารหรือแบ่งปันทรัพยากรเช่นหน่วยความจำหรือ I / O

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

หวังว่าด้วยคำจำกัดความเหล่านี้และการวิจัยเพิ่มเติมโดยใช้พื้นฐานเหล่านี้จะช่วยให้คุณเข้าใจ


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

@ David อาจจะไม่ใช่คำตอบสำหรับคำถามของฉันโดยตรง แต่ฉันก็ยังรู้สึกว่าได้เรียนรู้จากการอ่าน
Nick Heiner

1

การใช้เธรดในอุดมคตินั้นแท้จริงแล้วต่อหนึ่งคอร์

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

นอกจากนี้ภาษาการเขียนโปรแกรมทั่วไปทำให้การใช้ 1 เธรดต่อ CPU ค่อนข้างยาก ภาษาที่ออกแบบมาพร้อมกัน (เช่น Erlang) สามารถช่วยให้ไม่ใช้หัวข้อเพิ่มเติมได้ง่ายขึ้น


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

@Nick Bastin: ใช่ แต่มีประสิทธิภาพมากกว่าในการรวมภารกิจเหล่านั้นลงในคิวงานและเรียกใช้งานจากคิวนั้น (หรือกลยุทธ์ที่คล้ายคลึงกัน) เพื่อประสิทธิภาพที่ดีที่สุด 1 เธรดต่อคอร์เต้นได้ทั้งหมดเนื่องจากป้องกันการโอเวอร์เฮดจากการสลับบริบทที่ไม่จำเป็น ไม่ว่าจะเกิดอะไรขึ้นงานประจำงวดจะต้องขโมยคอร์ในขณะที่ 'แอคทีฟ' เพราะซีพียูสามารถทำงานได้เพียงหนึ่งงานต่อคอร์เท่านั้น
kyoryu

@Nick Bastin: น่าเสียดายที่ฉันได้กล่าวไว้ในคำตอบหลัก ๆ แล้วภาษาที่ทันสมัยส่วนใหญ่ไม่ค่อยให้ความสำคัญกับการใช้งานระบบที่ทำได้อย่างมีประสิทธิภาพซึ่งไม่ได้เป็นเรื่องเล็กน้อย - คุณจบลงด้วยการต่อสู้กับการใช้ภาษาทั่วไป
kyoryu

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

@Nick Bastin: ดังนั้นเพื่อสรุปหนึ่งเธรดต่อคอร์เหมาะอย่างยิ่ง แต่จริงๆแล้วการบรรลุนั้นไม่น่าเป็นไปได้ ฉันควรจะแข็งแกร่งกว่า 'ค่อนข้างยาก' เมื่อพูดถึงความเป็นไปได้ที่จะบรรลุสิ่งนั้นจริง ๆ
kyoryu

1

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

ปกติแล้วนี่ไม่ใช่ปัญหามากนัก (หากเป็นปัญหา OS หรือ API ควรมาพร้อมกับโหมดการทำงานแบบอะซิงโครนัสทางเลือกเช่น:) select(2)เนื่องจากอาจเป็นไปได้ว่าเธรดกำลังจะหลับระหว่างรอ I / เสร็จสิ้น ในทางกลับกันหากมีบางสิ่งที่กำลังทำการคำนวณอย่างหนักคุณต้องใส่ไว้ในเธรดแยกต่างหากนอกเหนือจากการพูดเธรด GUI (เว้นแต่คุณจะเพลิดเพลินกับการทำมัลติเพล็กซ์ด้วยตนเอง)


1

ฉันรู้ว่านี่เป็นคำถามเก่าแก่ที่มีคำตอบที่ดีมากมาย แต่ฉันมาที่นี่เพื่อชี้ให้เห็นสิ่งที่สำคัญในสภาพแวดล้อมปัจจุบัน:

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


0

ในการตอบสนองต่อการคาดเดาครั้งแรกของคุณ: เครื่องมัลติคอร์สามารถเรียกใช้หลายกระบวนการพร้อมกันไม่ใช่แค่หลายเธรดของกระบวนการเดียว

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

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


-5

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


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

-8

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

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

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


7
ไม่ใช่ (ค่อนข้าง!) มีค่า -1 แต่อย่างจริงจังนั่นเป็นเรื่องที่น่ารังเกียจที่สุดเท่าที่ฉันเคยได้ยินใครพูดเรื่องนี้ในเรื่องที่น่ารังเกียจ ยกตัวอย่างเช่นฉันไม่มีปัญหาในการใช้เครื่องสถานะ ไม่มีเลย ฉันไม่ชอบที่จะใช้มันเมื่อมีเครื่องมืออื่น ๆ ที่ทิ้งไว้ให้ชัดเจนและง่ายต่อการบำรุงรักษาโค้ด เครื่องจักรของรัฐมีสถานที่ของพวกเขาและในสถานที่เหล่านั้นพวกเขาไม่สามารถจับคู่ได้ การเชื่อมโยงการดำเนินการที่ใช้ CPU มากกับการอัพเดต GUI ไม่ใช่หนึ่งในสถานที่เหล่านั้น อย่างน้อย coroutines เป็นทางเลือกที่ดีกว่าที่นั่นด้วยเกลียวเป็นดียิ่งขึ้น
เพียงความคิดเห็นที่ถูกต้องของฉัน

สำหรับทุกคนที่กำลังแก้ไขคำตอบของฉันนี่ไม่ใช่ข้อโต้แย้งในการใช้เธรด! หากคุณสามารถเขียนรหัสเครื่องรัฐได้อย่างยอดเยี่ยมและตรวจสอบให้แน่ใจว่าการเรียกใช้เครื่องรัฐในหัวข้อแยกต่างหากแม้ว่าคุณไม่จำเป็นต้องทำก็ตาม ความคิดเห็นของฉันคือบ่อยครั้งที่ตัวเลือกในการใช้เธรดนั้นถูกสร้างขึ้นจากความต้องการที่จะหลีกเลี่ยงการออกแบบเครื่องจักรของรัฐซึ่งโปรแกรมเมอร์จำนวนมากพิจารณาว่า "ยากเกินไป" แทนที่จะใช้เพื่อประโยชน์อื่นใด
. GitHub หยุดช่วย ICE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.