ความแตกต่างระหว่าง "coroutine" และ "ด้าย"?


คำตอบ:


122

Coroutines เป็นรูปแบบของการประมวลผลตามลำดับ: มีเพียงอันเดียวเท่านั้นที่ดำเนินการในเวลาที่กำหนด

เธรดคือรูปแบบของการประมวลผลที่เกิดขึ้นพร้อมกัน (อย่างน้อยที่สุดแนวคิด): หลายเธรดอาจกำลังดำเนินการในเวลาใดก็ตาม (เดิมบน CPU เดียว, เครื่อง single-core พร้อมกันที่จำลองด้วยความช่วยเหลือจากระบบปฏิบัติการ - ปัจจุบันตั้งแต่ดังนั้นหลายเครื่องมีหลาย CPU และ / หรือแบบ multi-core, หัวข้อจะพฤตินัยจะดำเนินการพร้อมกัน ไม่เพียง "แนวคิด")


188

อ่านครั้งแรก: เห็นพ้องด้วยกับความเท่าเทียม - ความแตกต่างคืออะไร?

การเกิดขึ้นพร้อมกันคือการแยกของงานเพื่อให้การดำเนินการ interleaved Parallelism เป็นการทำงานหลายชิ้นพร้อมกันเพื่อเพิ่มความเร็ว - https://github.com/servo/servo/wiki/Design

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

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

ตรงกันข้ามกับเธรดซึ่งเป็น pre-emptive สวิตช์ coroutine นั้นทำงานร่วมกัน (โปรแกรมเมอร์ควบคุมเมื่อสวิตช์เกิดขึ้น) เคอร์เนลไม่ได้เกี่ยวข้องกับสวิตช์ coroutine - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html

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

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

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

Coroutinesและ / หรือเครื่องกำเนิดไฟฟ้าสามารถใช้ในการใช้ฟังก์ชั่นความร่วมมือ แทนที่จะถูกรันบนเคอร์นัลเธรดและกำหนดเวลาโดยระบบปฏิบัติการพวกมันจะรันในเธรดเดี่ยวจนกว่าพวกเขาจะให้ผลลัพธ์หรือเสร็จสิ้นยอมให้ทำหน้าที่อื่น ๆ ตามที่โปรแกรมเมอร์กำหนดไว้ ภาษากับเครื่องกำเนิดเช่น Python และ ECMAScript 6 สามารถใช้เพื่อสร้าง coroutines Async / await (เห็นใน C #, Python, ECMAscript 7, Rust) เป็นนามธรรมที่สร้างขึ้นจากฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่สร้างอนาคต / สัญญา

ในบางบริบทcoroutinesอาจอ้างถึงฟังก์ชั่นที่กองซ้อนในขณะที่เครื่องกำเนิดไฟฟ้าอาจหมายถึงฟังก์ชั่นที่ซ้อนกัน

Fibres , เธรดที่มีน้ำหนักเบาและเธรดสีเขียวเป็นชื่ออื่นสำหรับ coroutine หรือสิ่งที่คล้ายกับ coroutine บางครั้งพวกเขาอาจมีลักษณะเหมือนกันกับเธรดระบบปฏิบัติการในภาษาการเขียนโปรแกรม แต่โดยทั่วไปแล้วพวกเขาจะไม่ได้ทำงานแบบขนานเหมือนเธรดจริงและทำงานแทน coroutines (อาจมีลักษณะเฉพาะทางเทคนิคที่เฉพาะเจาะจงมากขึ้นหรือความแตกต่างระหว่างแนวคิดเหล่านี้ขึ้นอยู่กับภาษาหรือการใช้งาน)

ตัวอย่างเช่น Java มี " หัวข้อสีเขียว "; เหล่านี้คือเธรดที่กำหนดเวลาโดย Java virtual machine (JVM) แทนที่จะเป็นแบบดั้งเดิมบนเธรดเคอร์เนลของระบบปฏิบัติการ สิ่งเหล่านี้ไม่ได้ทำงานแบบขนานหรือใช้ประโยชน์จากโปรเซสเซอร์ / คอร์หลายตัว - เนื่องจากจะต้องใช้เธรดดั้งเดิม! เนื่องจากไม่มีการกำหนดตารางเวลาโดยระบบปฏิบัติการจึงเป็นเหมือน coroutines มากกว่าเคอร์เนลเธรด กรีนเธรดคือสิ่งที่ Java ใช้จนกระทั่งเธรดดั้งเดิมถูกนำเข้าสู่ Java 1.2

เธรดใช้ทรัพยากร ใน JVM แต่ละเธรดจะมีสแต็กของตนเองซึ่งโดยทั่วไปจะมีขนาด 1MB 64k เป็นจำนวนพื้นที่สแต็กน้อยที่สุดที่อนุญาตต่อเธรดใน JVM ขนาดเธรดสแต็กสามารถกำหนดค่าบนบรรทัดคำสั่งสำหรับ JVM แม้จะมีชื่อเธรดไม่ว่างเนื่องจากทรัพยากรการใช้งานเช่นแต่ละเธรดที่ต้องการสแต็กของตนเองหน่วยเก็บข้อมูลเธรดโลคัล (ถ้ามี) และต้นทุนของการทำให้ใช้แคชการกำหนดเวลาเธรด / การสลับบริบท นี่เป็นส่วนหนึ่งของสาเหตุที่ทำให้ coroutines กลายเป็นที่นิยมสำหรับแอพพลิเคชั่นที่สำคัญและมีประสิทธิภาพสูง

Mac OS จะอนุญาตให้กระบวนการจัดสรรเธรดประมาณ 2000 เธรดเท่านั้นและ Linux จะจัดสรรสแต็ก 8MB ต่อเธรดและจะอนุญาตเฉพาะเธรดจำนวนมากที่พอดีกับ RAM จริง

ดังนั้นเธรดมีน้ำหนักมากที่สุด (ในแง่ของการใช้หน่วยความจำและเวลาการสลับบริบท) จากนั้น coroutines และในที่สุดเครื่องกำเนิดไฟฟ้าก็จะมีน้ำหนักเบาที่สุด


2
+1 แต่คำตอบนี้อาจได้รับประโยชน์จากการอ้างอิงบางอย่าง
kojiro

1
หัวข้อสีเขียวเป็นสิ่งที่แตกต่างจาก coroutines พวกเขาไม่ได้? แม้แต่เส้นใยก็มีความแตกต่าง ดูprogrammers.stackexchange.com/questions/254140/…

113

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

ก่อนอื่นถ้า coroutines ทำงานพร้อมกัน (ไม่ขนาน ) ทำไมใครถึงชอบมันมากกว่าเธรด?

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

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

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

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

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

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

และในที่สุด co-routines ก็ได้รับความสนใจเป็นอย่างมากเพราะในบางภาษาการเขียนโปรแกรม (เช่น Python) เธรดของคุณไม่สามารถทำงานแบบขนานได้ - พวกมันทำงานพร้อมกันเหมือนกับ coroutines แต่ไม่มีหน่วยความจำต่ำและค่าใช้จ่ายในการตั้งเวลาว่าง


2
วิธีการสลับไปยังงานอื่นใน coroutines เมื่อเราพบการดำเนินการบล็อก?
Narcisse Doudieu Siewe

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

@MartinKonecny ​​หัวข้อ C ++ ล่าสุดเป็นไปตามแนวทางที่คุณกล่าวถึงหรือไม่
Nikos

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

19

ในคำ: ใบจอง Coroutines ทำตัวเหมือนนักเล่นกลที่คอยส่งต่อคะแนนที่ถูกซ้อมมาอย่างต่อเนื่อง เธรด (เธรดที่แท้จริง) สามารถถูกขัดจังหวะได้เกือบทุกจุดแล้วกลับมาทำงานต่อในภายหลัง แน่นอนว่าสิ่งนี้นำมาซึ่งปัญหาความขัดแย้งด้านทรัพยากรทุกประเภทดังนั้น GIL ​​ที่น่าอับอายของ Python - Global Interpreter Lock

การปรับใช้เธรดจำนวนมากนั้นแท้จริงแล้วเหมือนกับ coroutines


9

ขึ้นอยู่กับภาษาที่คุณใช้ ยกตัวอย่างเช่นใน Lua พวกมันเหมือนกัน (เรียกว่า coroutine ชนิดตัวแปรthread)

โดยทั่วไปแม้ว่า coroutines จะใช้ความสมัครใจในการที่โปรแกรมเมอร์จะเป็นผู้ตัดสินใจว่าจะyieldให้การควบคุมไปที่รูทีนอื่น

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


0

12 ปีหลังการอภิปราย แต่ coroutine มีคำอธิบายในชื่อ Coroutine สามารถย่อยสลายได้ใน Co และ Routine

รูทีนในบริบทนี้เป็นเพียงลำดับของการดำเนินการ / การกระทำและโดยการดำเนินการ / การประมวลผลตามปกติลำดับของการดำเนินการจะถูกดำเนินการทีละตัวในลำดับเดียวกันตามที่ระบุ

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

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

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

ไม่สำคัญว่าเพื่อนร่วมกิจวัตรจะระงับตัวเองอย่างไร ย้อนกลับไปใน Windows 3.1 int 03 ได้รวมเข้ากับโปรแกรมใด ๆ (หรือจะต้องมีอยู่ในนั้น) และใน C # เราเพิ่มผลผลิต

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