การวนรอบเหตุการณ์เป็นเพียงการวนรอบ / ขณะที่การหยั่งสัญญาณที่ปรับปรุงแล้ว


55

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

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


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


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

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

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

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


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

1
ท้ายที่สุดแล้วแม้ว่าภาษาจะสร้างสิ่งที่เป็นนามธรรมออกไป
GrandmasterB

@SteveEvers ในลิงค์ wiki ของคุณ จะEventSourceทำอย่างไรหากไม่ได้ทำการสำรวจอินพุตคีย์บอร์ด?
TheMeaningfulEngineer

@Alan: มันอาจจะทำอะไรก็ได้ แต่สำหรับการป้อนแป้นพิมพ์โดยเฉพาะมีอยู่ API ที่จะลงทะเบียนแอปพลิเคชันของคุณเป็นฟังเหตุการณ์แป้นพิมพ์ซึ่งคุณสามารถใช้เป็นแหล่งที่มาของเหตุการณ์ของคุณโดยไม่เกี่ยวข้องกับการสำรวจ แน่นอนถ้าคุณลงไปมากพอ USB จะทำการสำรวจเสมอแต่สมมติว่าเรากำลังใช้แป้นพิมพ์ PS / 2 ซึ่งถูกขัดจังหวะการขับเคลื่อนและจากนั้นคุณมีการป้อนแป้นพิมพ์สแต็คตามเหตุการณ์ที่ไม่มีการสำรวจ
Phoshi

ฉันมีคำถามนี้มาหลายปีแล้ว แต่ฉันไม่สามารถบอกได้ว่าทำไมฉันไม่เคยถามคำถาม ขอบคุณตอนนี้ฉันพอใจกับความเข้าใจ @Karl Bielefeldt ทำให้ฉันรู้แจ้งด้วย
0xc0de

คำตอบ:


54

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

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

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


4
การเปลี่ยนแปลงของแรงดันไฟฟ้าบนสายไฟขัดจังหวะหรือไม่ เหตุการณ์นั้นสามารถเกิดขึ้นได้ด้วยตัวเองหรือเราจะต้องสำรวจพินเพื่อหาค่าแรงดันไฟฟ้าหรือไม่?
TheMeaningfulEngineer

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

2
Most event loops will blockฉันสับสนกับคำสั่ง สิ่งนี้เหมาะสมอย่างไรกับ "กระบวนการวนรอบเหตุการณ์ตรงข้ามกับการใช้เธรดใช้การโทรแบบอะซิงโครนัสที่ไม่บล็อก"
TheMeaningfulEngineer

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

1
ฉันคิดว่าคุณสามารถดูได้ด้วยวิธีนี้ แต่มันเป็นมัลติเธรดในไลบรารีและ / หรือระบบปฏิบัติการมากกว่ารหัสแอปพลิเคชัน เหตุการณ์ลูปเป็นนามธรรมปัญหาการประสานไปสำหรับนักพัฒนาแอปพลิเคชัน
Karl Bielefeldt

13

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

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

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

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

ฉันคิดว่านี่เป็น:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

และระหว่างsignalและในครั้งถัดไปที่ตัวจัดการรันไม่มีแนวคิดที่จะรันโค้ดหรือวนลูป


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

หากรหัสของคุณมีการวนซ้ำforever: { select(); do stuff; }คุณจะเข้าสู่การแข่งขันทุกครั้งที่ผ่านการวนซ้ำ ไม่ว่าคุณจะทำอย่างนั้นซ้ำ ๆ จากเธรดเดี่ยวหรือขนานกับเธรดหรือตัวประมวลผลแยกกันฉันคิดว่าแต่ละเหตุการณ์เป็นของตัวเอง ตัวอย่างเช่นเว็บเบราว์เซอร์เป็นโปรแกรมแบบมัลติเธรดที่มีหลายลูปเหตุการณ์ในเธรดแยกต่างหากอย่างน้อยหนึ่งรายการสำหรับ UI และอีกอันสำหรับแต่ละเพจที่กำลังดาวน์โหลด คำถามที่ฉันถามเมื่อการเข้ารหัสคือ "ฉันจะประมวลผลเหตุการณ์เร็วพอได้อย่างไร" บางครั้งคำตอบคือการวนซ้ำบางครั้งเธรดมักจะรวมกัน
cxw

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

8

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

ในขณะที่จนถึงสำหรับ ฯลฯ ลูปเป็นลูปการเลือกตั้ง

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

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

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

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

เซมาฟอเรสสามารถเปลี่ยนแปลงได้ภายใต้การควบคุมซอฟต์แวร์และสามารถให้กลไกการส่งสัญญาณที่รวดเร็วและง่ายดายระหว่างกระบวนการซอฟต์แวร์

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

ฉันออกไปเยอะ แต่ต้องหยุดที่ไหนซักแห่ง โปรดถามว่าคุณต้องการรายละเอียดเพิ่มเติมหรือไม่


7

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


7

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

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

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


1
+1 ที่ตรงไปตรงมา แต่ความสำคัญอยู่ที่ "รอ"; การทำงานของรหัสนี้จะหยุดในบรรทัดแรกของลูปและไม่ดำเนินการต่อจนกว่าจะมีเหตุการณ์เกิดขึ้น สิ่งนี้แตกต่างจากการวนซ้ำแบบไม่ว่างที่ตรวจสอบเหตุการณ์ซ้ำ ๆ จนกระทั่งเกิดเหตุการณ์หนึ่งขึ้น
Tom Panning

1

ทริกเกอร์เหตุการณ์ไม่ได้รับการจัดการทั้งหมดในลูป วิธีที่ฉันมักจะเขียนเอนจินเหตุการณ์ของตัวเองจะเป็นเช่นนี้:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

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

ในทางทฤษฎีเหตุการณ์สำคัญระดับ OS สามารถใช้เทคนิคเดียวกัน (แม้ว่าฉันคิดว่าพวกเขามักจะทำการสำรวจความคิดเห็นแทนฉันแค่คาดเดาที่นี่) หากระบบปฏิบัติการเปิดเผยregisterListenerAPI บางประเภท

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