โครงสร้างสแต็กใช้สำหรับกระบวนการ async หรือไม่?


10

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

จากคำตอบของเขา:

สแต็คเป็นส่วนหนึ่งของการทำให้ใหม่ของความต่อเนื่องในภาษาโดยไม่ต้อง coroutines

โดยเฉพาะอย่างยิ่งส่วนที่ไร้ coroutinesของนี้มีฉันสงสัย

เขาอธิบายเพิ่มเติมอีกเล็กน้อยที่นี่:

Coroutine เป็นฟังก์ชั่นที่จำได้ว่าอยู่ที่ไหนให้ผลการควบคุมไปยัง coroutine อีกระยะหนึ่งแล้วกลับมาทำงานต่อที่พวกมันทิ้งไว้ในภายหลัง แต่ไม่จำเป็นว่าจะต้องเกิดขึ้นทันทีหลังจาก coroutine ที่เพิ่งเรียกว่า คิดว่า "คืนผลตอบแทน" หรือ "รอ" ใน C # ซึ่งจะต้องจำไว้ว่าพวกเขาอยู่ที่ไหนเมื่อมีการร้องขอรายการถัดไปหรือการดำเนินการแบบอะซิงโครนัสเสร็จสมบูรณ์ ภาษาที่มี coroutines หรือฟีเจอร์ภาษาที่คล้ายกันนั้นต้องการโครงสร้างข้อมูลที่สูงกว่าสแต็กเพื่อที่จะใช้งานต่อเนื่อง

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

สแต็คจะหายไปเมื่อเทคโนโลยีก้าวหน้าไปหรือไม่? แทนที่มันคืออะไร มันเป็นลูกผสมหรือเปล่า (เช่นโปรแกรม. NET ของฉันจะใช้สแต็กจนกว่าจะถึงการเรียกใช้ async จากนั้นสลับไปยังโครงสร้างอื่น ๆ จนกว่าจะเสร็จสมบูรณ์ ณ จุดที่สแต็กนั้นกลับมาอยู่ในสถานะที่สามารถมั่นใจได้ในรายการถัดไป ฯลฯ )

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

คำตอบ:


14

มันเป็นลูกผสมหรือเปล่า (เช่นโปรแกรม. NET ของฉันจะใช้สแต็กจนกว่าจะถึงการเรียกใช้ async จากนั้นสลับไปยังโครงสร้างอื่น ๆ จนกว่าจะเสร็จสมบูรณ์ ณ จุดที่สแต็กนั้นกลับมาอยู่ในสถานะที่สามารถมั่นใจได้ในรายการถัดไป ฯลฯ )

ใช่แล้ว

สมมติว่าเรามี

async void MyButton_OnClick() { await Foo(); Bar(); }
async Task Foo() { await Task.Delay(123); Blah(); }

ต่อไปนี้เป็นคำอธิบายที่ง่ายมากของวิธีการแก้ไขต่อเนื่อง รหัสจริงมีความซับซ้อนมากกว่านี้มาก

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

ตัวจัดการการคลิกเรียก Foo () โดยวางที่อยู่ผู้ส่งของตัวเองไว้บนสแต็ก นั่นคือความต่อเนื่องของ Foo คือส่วนที่เหลือของตัวจัดการการคลิก

Foo เรียกใช้ Task.Delay วางที่อยู่ผู้ส่งคืนลงในสแต็ก

Task.Delay ทำสิ่งที่ต้องใช้เวทมนตร์เพื่อส่งคืนภารกิจทันที สแต็กถูกตอกและเรากลับมาที่ฟู

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

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

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

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

เกิดอะไรขึ้น นี่คือบิตหากิน ความต่อเนื่องของงานล่าช้าไม่เพียง แต่เรียก Blah () มันต้องเรียกการโทรไปที่ Bar ()แต่ภารกิจนั้นไม่รู้เกี่ยวกับ Bar!

Foo จริงสร้างตัวแทนว่า (1) เรียก Blah () และ (2) เรียกความต่อเนื่องของงานที่สร้างขึ้นฟูและกลับมามอบให้จัดการเหตุการณ์ นั่นคือวิธีที่เราเรียกผู้ได้รับมอบหมายที่เรียก Bar ()

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

ว่าสถานการณ์เหล่านี้สูงเกินไปสำหรับสแต็กทำให้รู้สึกที่สมบูรณ์แบบ แต่สิ่งที่แทนที่สแต็ค?

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

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

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

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

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


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

3
@ jdl134679: ฉันขอแนะนำให้คุณทำเครื่องหมายสิ่งที่เป็นคำตอบหากคุณรู้สึกว่าคำถามของคุณได้รับคำตอบแล้ว; ที่ส่งสัญญาณว่าทุกคนควรจะมาที่นี่ถ้าพวกเขาต้องการไปอ่านคำตอบที่ดีมากกว่าการเขียนอย่างใดอย่างหนึ่ง (แน่นอนว่าการเขียนคำตอบที่ดีนั้นได้รับการสนับสนุนเสมอ) ฉันไม่สนใจว่าใครจะได้เครื่องหมายถูก
Eric Lippert

8

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

แต่สแต็คการโทรยังคงใช้กันอย่างแพร่หลายและโปรเซสเซอร์ปัจจุบันมีฮาร์ดแวร์เฉพาะ (สแต็ครีจิสเตอร์, แคชเครื่องจักร, .... ) โดยเฉพาะสำหรับการโทรสแต็ก นำไปใช้กับ call stack) แจ้งให้ทราบว่ามีกองแคชมิตร (และที่สำคัญมากสำหรับการทำงาน)

จวนการโทรยังคงอยู่ที่นี่ แต่ตอนนี้เรามีหลายคนและบางครั้ง call stack ถูกแบ่งออกเป็นกลุ่มเล็ก ๆ (เช่นหน้าละไม่กี่ 4Kbytes) ซึ่งบางครั้งก็มีการรวบรวมขยะหรือจัดสรรกองไว้ กลุ่มสแต็กเหล่านี้สามารถจัดระเบียบในรายการที่เชื่อมโยงบางส่วน (หรือโครงสร้างข้อมูลที่ซับซ้อนมากขึ้นเมื่อจำเป็น) ยกตัวอย่างเช่นGCCคอมไพเลอร์มี-fsplit-stackตัวเลือก (มีประโยชน์โดยเฉพาะอย่างยิ่งสำหรับการไปและ "goroutines" และ "กระบวนการ async" ของมัน) ด้วยสแต็คแบบแยกคุณสามารถมีหลายพันสแต็ก (และการทำ co-routines ง่ายขึ้น) ที่ทำจากเซ็กเมนต์สแต็กเล็ก ๆ เป็นล้าน ๆ ส่วนและ "การคลี่คลาย" สแต็กอาจเร็วกว่า ซ้อนกัน).

(กล่าวอีกนัยหนึ่งความแตกต่างระหว่างกอง & กองเป็นพร่ามัว แต่อาจต้องเปลี่ยนโปรแกรม - ทั้งหมดหรือเปลี่ยนการประชุมและคอมไพเลอร์เรียกไม่ลงรอยกัน )

ดูสิ่งนี้และสิ่งนั้นและบทความมากมาย(เช่นนี้ ) เกี่ยวกับการแปลง CPS อ่านยังเกี่ยวกับASLRและโทร / ซีซี อ่าน (และ STFW) เพิ่มเติมเกี่ยวกับต

การใช้งาน. CLR และ. NET อาจไม่มีการแปลง GC & CPS ที่ล้ำสมัยด้วยเหตุผลเชิงปฏิบัติมากมาย เป็นการแลกเปลี่ยนที่เกี่ยวข้องกับการแปลงโปรแกรมทั้งหมด (และความสะดวกในการใช้รูทีน C ในระดับต่ำและมีรหัสการรันไทม์ใน C หรือ C ++)

Chicken Schemeกำลังใช้สแต็คเครื่อง (หรือ C) แบบแปลกใหม่กับการแปลง CPS: การจัดสรรทุกครั้งเกิดขึ้นบนสแต็กและเมื่อมันมีขนาดใหญ่เกินไปขั้นตอนการคัดลอกและส่งต่อ GC generational เกิดขึ้นเพื่อย้ายค่าการจัดสรรสแต็กล่าสุด ความต่อเนื่องในปัจจุบัน) setjmpไปกองแล้วสแต็คจะลดลงอย่างเห็นได้ชัดที่มีขนาดใหญ่


อ่านยังSICP , การเขียนโปรแกรมภาษาเน้นที่มังกรหนังสือ , เสียงกระเพื่อมในชิ้นเล็ก ๆ


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