Coroutines ขึ้นอยู่กับแนวคิดของเครื่องกำเนิดไฟฟ้าโดยที่ฟังก์ชันสามารถให้ค่าได้ และกลับมาดำเนินการต่อเพื่อดำเนินการต่อไปในภายหลัง Coroutines มอบเครื่องมืออันทรงพลังในการจัดการการทำงานแบบอะซิงโครนัสและสามารถปรับปรุงคุณภาพโดยรวมของโค้ดของคุณได้อย่างมาก
การใช้โครูทีน
Coroutines จำเป็นด้วยเหตุผลหลายประการในการเขียนโปรแกรมสมัยใหม่ โดยเฉพาะในภาษาเช่น C++ ต่อไปนี้เป็นเหตุผลสำคัญบางประการว่าทำไม Coroutines จึงมีประโยชน์:
Coroutines มอบโซลูชันที่หรูหราสำหรับการเขียนโปรแกรมแบบอะซิงโครนัส ช่วยให้สามารถสร้างโค้ดที่ปรากฏตามลำดับและบล็อกได้ ซึ่งง่ายต่อการให้เหตุผลและเข้าใจ Coroutines สามารถระงับการดำเนินการ ณ จุดใดจุดหนึ่งโดยไม่ปิดกั้นเธรด ทำให้สามารถทำงานอื่นๆ แบบขนานได้ ด้วยเหตุนี้ ทรัพยากรระบบอาจถูกนำมาใช้อย่างมีประสิทธิภาพมากขึ้น และการตอบสนองจะเพิ่มขึ้นในแอปพลิเคชันที่เกี่ยวข้องกับการดำเนินการ I/O หรือการรอเหตุการณ์ภายนอก
พวกเขาอาจทำให้โค้ดเข้าใจและบำรุงรักษาได้ง่ายขึ้น ด้วยการขจัดสายการเรียกกลับที่ซับซ้อนหรือเครื่องสถานะ Coroutines ช่วยให้โค้ดสามารถเขียนในรูปแบบเชิงเส้นและเป็นลำดับมากขึ้น สิ่งนี้ช่วยปรับปรุงการจัดระเบียบโค้ด ลดการซ้อน และทำให้เข้าใจตรรกะได้ง่าย
Coroutines จัดเตรียมวิธีการที่มีโครงสร้างเพื่อจัดการกับภาวะเห็นพ้องกันและความเท่าเทียม ช่วยให้คุณสามารถแสดงรูปแบบการประสานงานที่ซับซ้อนและเวิร์กโฟลว์แบบอะซิงโครนัสโดยใช้ไวยากรณ์ที่ใช้งานง่ายยิ่งขึ้น ต่างจากรุ่นเธรดแบบดั้งเดิมที่เธรดอาจถูกบล็อก coroutines สามารถเพิ่มทรัพยากรระบบและเปิดใช้งานมัลติทาสก์ได้อย่างมีประสิทธิภาพ
มาสร้างตัวอย่างเพื่อสาธิตการใช้งาน Coroutines ใน C++
ตัวอย่างที่ 1: Coroutines พื้นฐาน
ตัวอย่าง Coroutines พื้นฐานมีดังต่อไปนี้:
#รวม
#รวม <โครูทีน>
โครงสร้าง นี้Corout {
โครงสร้าง สัญญา_ประเภท {
ThisCorout get_return_object ( ) { กลับ { } ; }
มาตรฐาน :: ระงับ_ไม่เคย Initial_suspend ( ) { กลับ { } ; }
มาตรฐาน :: ระงับ_ไม่เคย สุดท้าย_ระงับ ( ) ไม่ยกเว้น { กลับ { } ; }
เป็นโมฆะ ไม่สามารถจัดการ_ข้อยกเว้นได้ ( ) { }
เป็นโมฆะ กลับ_เป็นโมฆะ ( ) { }
} ;
บูล await_ready ( ) { กลับ เท็จ ; }
เป็นโมฆะ รอ_ระงับ ( มาตรฐาน :: coroutine_handle <> ชม. ) { }
เป็นโมฆะ await_resume ( ) { มาตรฐาน :: ศาล << 'โครูทีนกลับมาทำงานต่อแล้ว' << มาตรฐาน :: สิ้นสุด ; }
} ;
นี้Corout foo ( ) {
มาตรฐาน :: ศาล << “โครูทีนได้เริ่มต้นแล้ว” << มาตรฐาน :: สิ้นสุด ;
co_await มาตรฐาน :: ระงับ_เสมอ { } ;
ร่วม_กลับ ;
}
ภายใน หลัก ( ) {
อัตโนมัติ cr = ฟู ( ) ;
มาตรฐาน :: ศาล << “โครูทีนถูกสร้างขึ้นแล้ว” << มาตรฐาน :: สิ้นสุด ;
cr. await_resume ( ) ;
มาตรฐาน :: ศาล << “โครูทีนเสร็จแล้ว” << มาตรฐาน :: สิ้นสุด ;
กลับ 0 ;
}
มาดูโค้ดที่ให้ไว้ก่อนหน้านี้และอธิบายโดยละเอียด:
หลังจากรวมไฟล์ส่วนหัวที่จำเป็นแล้ว เราจะกำหนดโครงสร้าง 'ThisCorout' ที่แสดงถึง Coroutine ภายใน 'ThisCorout' มีการกำหนดโครงสร้างอื่นซึ่งเป็น 'promise_type' ที่จัดการสัญญา Coroutine โครงสร้างนี้มีฟังก์ชันต่างๆ ที่จำเป็นสำหรับเครื่องจักรโครูทีน
ภายในวงเล็บ เราใช้ฟังก์ชัน get_return_object() มันจะส่งคืนวัตถุ coroutine เอง ในกรณีนี้ จะส่งคืนออบเจ็กต์ 'ThisCorout' ที่ว่างเปล่า จากนั้นฟังก์ชัน Initial_suspend() จะถูกเรียกใช้ซึ่งจะกำหนดลักษณะการทำงานเมื่อเริ่มต้น Coroutine เป็นครั้งแรก std::suspend_never หมายความว่าไม่ควรระงับ coroutine ในขั้นต้น
หลังจากนั้น เรามีฟังก์ชัน Final_suspend() ซึ่งจะกำหนดพฤติกรรมเมื่อ Coroutine กำลังจะเสร็จสิ้น std::suspend_never หมายความว่าไม่ควรระงับ coroutine ก่อนที่จะทำการสรุป
หาก Coroutine ส่งข้อยกเว้น ระบบจะเรียกใช้เมธอด unhandled_Exception() ในตัวอย่างนี้ เป็นฟังก์ชันว่าง แต่คุณสามารถจัดการข้อยกเว้นได้ตามต้องการ เมื่อ Coroutine สิ้นสุดลงโดยไม่ให้ค่า เมธอด return_void() จะถูกเรียกใช้ ในกรณีนี้ ก็เป็นฟังก์ชันว่างเช่นกัน
นอกจากนี้เรายังกำหนดฟังก์ชันสมาชิกสามรายการภายใน 'ThisCorout' ฟังก์ชัน await_ready() ถูกเรียกเพื่อตรวจสอบว่า Coroutine พร้อมที่จะดำเนินการต่อหรือไม่ ในตัวอย่างนี้ จะคืนค่าเท็จเสมอ ซึ่งบ่งชี้ว่าโครูทีนไม่พร้อมที่จะดำเนินการต่อทันที เมื่อ Coroutine จะถูกระงับ จะมีการเรียกเมธอด await_suspend() ตรงนี้เป็นฟังก์ชันว่างซึ่งหมายความว่าไม่จำเป็นต้องมีการระงับ โปรแกรมจะเรียก await_resume() เมื่อ coroutine กลับมาทำงานต่อหลังจากการระงับชั่วคราว มันแค่ส่งข้อความที่ระบุว่า coroutine กลับมาทำงานต่อแล้ว
บรรทัดถัดไปของโค้ดจะกำหนดฟังก์ชัน foo() coroutine ภายใน foo() เราเริ่มต้นด้วยการพิมพ์ข้อความที่ระบุว่า Coroutine ได้เริ่มต้นแล้ว จากนั้น co_await std::suspend_always{} จะถูกใช้เพื่อระงับ coroutine และระบุว่าสามารถดำเนินการต่อได้ในภายหลัง คำสั่ง co_return ใช้เพื่อสิ้นสุด Coroutine โดยไม่ส่งคืนค่าใดๆ
ในฟังก์ชัน main() เราสร้างวัตถุ “cr” ประเภท “ThisCorout” โดยการเรียก foo() สิ่งนี้จะสร้างและเริ่มต้นโครูทีน จากนั้นจะมีการพิมพ์ข้อความที่ระบุว่าสร้าง Coroutine แล้ว ต่อไป เราจะเรียก await_resume() บนออบเจ็กต์ coroutine “cr” เพื่อดำเนินการต่อไป ภายใน await_resume() ข้อความ “The Coroutine is resumed” จะถูกพิมพ์ออกมา สุดท้ายนี้ เราจะแสดงข้อความที่ระบุว่าโครูทีนเสร็จสมบูรณ์ก่อนที่โปรแกรมจะสิ้นสุดลง
เมื่อคุณรันโปรแกรมนี้ ผลลัพธ์จะเป็นดังนี้:
ตัวอย่างที่ 2: Coroutine พร้อมพารามิเตอร์และการให้ผล
สำหรับภาพประกอบนี้ เราได้จัดเตรียมโค้ดที่สาธิตการใช้คอร์รูทีนพร้อมพารามิเตอร์และการให้ผลใน C++ เพื่อสร้างพฤติกรรมที่เหมือนตัวสร้างเพื่อสร้างลำดับของตัวเลข
#รวม#รวม <โครูทีน>
#รวม <เวกเตอร์>
โครงสร้าง ใหม่โครูทีน {
โครงสร้าง p_type {
มาตรฐาน :: เวกเตอร์ < ภายใน > ค่านิยม ;
ใหม่Coroutine get_return_object ( ) { กลับ { } ; }
มาตรฐาน :: ระงับ_เสมอ Initial_suspend ( ) { กลับ { } ; }
มาตรฐาน :: ระงับ_เสมอ สุดท้าย_ระงับ ( ) ไม่ยกเว้น { กลับ { } ; }
เป็นโมฆะ ไม่สามารถจัดการ_ข้อยกเว้นได้ ( ) { }
เป็นโมฆะ กลับ_เป็นโมฆะ ( ) { }
มาตรฐาน :: ระงับ_เสมอ อัตราผลตอบแทน_value ( ภายใน ค่า ) {
ค่านิยม ผลักดัน_กลับ ( ค่า ) ;
กลับ { } ;
}
} ;
มาตรฐาน :: เวกเตอร์ < ภายใน > ค่านิยม ;
โครงสร้าง ตัววนซ้ำ {
มาตรฐาน :: coroutine_handle <> คอรัส_แฮนด์ ;
ตัวดำเนินการบูล != ( ค่าคงที่ ตัววนซ้ำ & อื่น ) ค่าคงที่ { กลับ คอรัส_แฮนด์ != อื่น. คอรัส_แฮนด์ ; }
ตัววนซ้ำ & ตัวดำเนินการ ++ ( ) { คอรัส_แฮนด์ ประวัติย่อ ( ) ; กลับ * นี้ ; }
ภายใน ตัวดำเนินการ * ( ) ค่าคงที่ { กลับ คอรัส_แฮนด์ สัญญา ( ) . ค่านิยม [ 0 ] ; }
} ;
ตัววนซ้ำเริ่มต้น ( ) { กลับ ตัววนซ้ำ { มาตรฐาน :: coroutine_handle < p_type >:: จาก_สัญญา ( สัญญา ( ) ) } ; }
สิ้นสุดตัววนซ้ำ ( ) { กลับ ตัววนซ้ำ { nullptr } ; }
มาตรฐาน :: coroutine_handle < p_type > สัญญา ( ) { กลับ
มาตรฐาน :: coroutine_handle < p_type >:: จาก_สัญญา ( * นี้ ) ; }
} ;
ใหม่Coroutine สร้างตัวเลข ( ) {
ร่วมผลผลิต 5 ;
ร่วมผลผลิต 6 ;
ร่วมผลผลิต 7 ;
}
ภายใน หลัก ( ) {
ใหม่Coroutine nc = สร้างตัวเลข ( ) ;
สำหรับ ( ภายใน ค่า : : nc ) {
มาตรฐาน :: ศาล << ค่า << ' ' ;
}
มาตรฐาน :: ศาล << มาตรฐาน :: สิ้นสุด ;
กลับ 0 ;
}
ในโค้ดก่อนหน้านี้ โครงสร้าง NEWCoroutine แสดงถึงตัวสร้างที่ใช้ Coroutine มีโครงสร้าง 'p_type' ที่ซ้อนกันซึ่งทำหน้าที่เป็นประเภทสัญญาสำหรับ coroutine โครงสร้าง p_type กำหนดฟังก์ชันที่จำเป็นโดยเครื่องจักร Coroutine เช่น get_return_object(), Initial_suspend(), Final_suspend(), unhandled_Exception() และ return_void() โครงสร้าง p_type ยังมีฟังก์ชัน Yield_value(int value) ซึ่งใช้เพื่อให้ได้ค่าจาก Coroutine โดยจะเพิ่มค่าที่ระบุให้กับเวกเตอร์ค่า
โครงสร้าง NEWCoroutine รวมถึงตัวแปรสมาชิก std::vector
เราใช้ฟังก์ชัน beginning() เพื่อสร้างตัววนซ้ำที่จุดเริ่มต้นของ coroutine โดยรับ coro_handle จากสัญญา p_type ในขณะที่ฟังก์ชัน end() จะสร้างตัววนซ้ำที่แสดงถึงจุดสิ้นสุดของ coroutine และสร้างด้วย nullptr coro_handle หลังจากนั้น ฟังก์ชัน Promise() จะถูกนำมาใช้เพื่อส่งคืนประเภทสัญญาโดยสร้าง coroutine_handle จากสัญญา p_type ฟังก์ชัน GenerateNumbers() คือ Coroutine ที่ให้ค่าสามค่า ได้แก่ 5, 6 และ 7 โดยใช้คีย์เวิร์ด co_yield
ในฟังก์ชัน main() อินสแตนซ์ของ NEWCoroutine ชื่อ 'nc' จะถูกสร้างขึ้นโดยการเรียกใช้ GenerateNumbers() coroutine สิ่งนี้จะเริ่มต้นโครูทีนและบันทึกสถานะของมัน ลูป 'for' ตามช่วงใช้เพื่อวนซ้ำค่า 'nc' และแต่ละค่าจะถูกพิมพ์ซึ่งคั่นด้วยช่องว่างโดยใช้ std::cout
ผลลัพธ์ที่สร้างขึ้นจะเป็นดังนี้:
บทสรุป
บทความนี้สาธิตการใช้งาน Coroutines ใน C++ เราได้พูดคุยกันสองตัวอย่าง สำหรับภาพประกอบแรก โครูทีนพื้นฐานจะถูกสร้างขึ้นในโปรแกรม C++ โดยใช้ฟังก์ชันโครูทีน ในขณะที่การสาธิตครั้งที่สองดำเนินการโดยการใช้โครูทีนพร้อมพารามิเตอร์และยอมจำนนเพื่อสร้างพฤติกรรมเหมือนตัวกำเนิดเพื่อสร้างลำดับของตัวเลข