ที่จริงแล้ว async / คอยไม่ได้เป็นเวทมนตร์ หัวข้อเต็มค่อนข้างกว้าง แต่สำหรับคำตอบสำหรับคำถามของคุณฉันคิดว่าเราสามารถจัดการได้
มาจัดการกับเหตุการณ์การคลิกปุ่มง่ายๆในแอปพลิเคชัน Windows Forms:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
ฉันจะชัดเจน ไม่ได้พูดคุยเกี่ยวกับสิ่งที่เป็นGetSomethingAsync
อยู่ในตอนนี้กลับมา สมมุติว่านี่คือสิ่งที่จะทำให้เสร็จหลังจากนั้น 2 วินาที
ในโลกแบบดั้งเดิมที่ไม่ประสานเวลาตัวจัดการเหตุการณ์การคลิกปุ่มของคุณจะมีลักษณะดังนี้:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
เมื่อคุณคลิกปุ่มในแบบฟอร์มแอปพลิเคชันจะปรากฏขึ้นค้างประมาณ 2 วินาทีในขณะที่เรารอให้วิธีนี้เสร็จสมบูรณ์ สิ่งที่เกิดขึ้นคือ "message pump" ซึ่งโดยทั่วไปจะเป็น loop จะถูกบล็อค
ลูปนี้ถาม windows อย่างต่อเนื่อง "มีใครทำอะไรบางอย่างเช่นเลื่อนเมาส์คลิกที่บางสิ่งบางอย่างหรือไม่ฉันต้องทาสีบางสิ่งบางอย่างหรือไม่ถ้าใช่บอกฉัน!" แล้วประมวลผลว่า "บางสิ่ง" การวนซ้ำนี้มีข้อความว่าผู้ใช้คลิกที่ "button1" (หรือประเภทข้อความเทียบเท่าจาก Windows) และสิ้นสุดการเรียกbutton1_Click
วิธีการของเราด้านบน จนกว่าเมธอดนี้จะส่งคืนลูปนี้จะหยุดรอ ขั้นตอนนี้ใช้เวลา 2 วินาทีและในระหว่างนี้จะไม่มีการประมวลผลข้อความ
สิ่งส่วนใหญ่ที่จัดการกับ windows จะทำโดยใช้ข้อความซึ่งหมายความว่าถ้าลูปข้อความหยุดสูบข้อความแม้เพียงแค่หนึ่งวินาทีก็จะเห็นได้อย่างรวดเร็วโดยผู้ใช้ ตัวอย่างเช่นถ้าคุณย้ายแผ่นจดบันทึกหรือโปรแกรมอื่น ๆ ที่อยู่ด้านบนของโปรแกรมของคุณเองและจากนั้นออกไปอีกครั้งข้อความสีเพี้ยนจะถูกส่งไปยังโปรแกรมของคุณเพื่อระบุว่าบริเวณใดของหน้าต่างที่ปรากฏขึ้นทันที หากลูปข้อความที่ประมวลผลข้อความเหล่านี้กำลังรอบางสิ่งอยู่ถูกบล็อกจะไม่มีการทาสี
ดังนั้นถ้าในตัวอย่างแรกasync/await
ไม่ได้สร้างเธรดใหม่มันจะทำอย่างไร
สิ่งที่เกิดขึ้นก็คือวิธีการของคุณแบ่งออกเป็นสองวิธี นี่เป็นหนึ่งในหัวข้อแบบกว้าง ๆ ของสิ่งต่าง ๆ ดังนั้นฉันจะไม่ลงรายละเอียดมากเกินไป แต่พอเพียงที่จะบอกว่าวิธีแบ่งออกเป็นสองสิ่งนี้:
- รหัสทั้งหมดที่นำไปสู่
await
รวมถึงการโทรไปยังGetSomethingAsync
- รหัสทั้งหมดดังต่อไปนี้
await
ภาพประกอบ:
code... code... code... await X(); ... code... code... code...
จัดใหม่:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
โดยทั่วไปวิธีการดำเนินการเช่นนี้:
- มันทำทุกอย่างได้สูงสุด
await
มันเรียกGetSomethingAsync
วิธีการนี้ซึ่งทำสิ่งนั้นและคืนสิ่งที่จะทำให้เสร็จสมบูรณ์ภายใน 2 วินาทีในอนาคต
จนถึงตอนนี้เรายังอยู่ในการโทรเดิมไปที่ button1_Click ซึ่งเกิดขึ้นในเธรดหลักที่เรียกว่าจากลูปข้อความ หากรหัสที่นำไปสู่การawait
ใช้เวลานาน UI จะยังคงค้าง ในตัวอย่างของเราไม่มาก
สิ่งที่await
คำหลักพร้อมกับมายากลคอมไพเลอร์ที่ฉลาดทำก็คือมันเป็นอะไรบางอย่างเช่น "โอเคคุณรู้อะไรฉันจะกลับมาจากปุ่มคลิกตัวจัดการเหตุการณ์ที่นี่เมื่อคุณ (ในสิ่งที่เรา ' กำลังรออีกรอบ) ทำให้ฉันรู้เพราะฉันยังมีรหัสเหลือให้เปิดใช้งาน "
ที่จริงแล้วมันจะให้คลาส SynchronizationContextรู้ว่ามันทำเสร็จแล้วซึ่งขึ้นอยู่กับบริบทการซิงโครไนซ์ที่เกิดขึ้นจริงที่กำลังเล่นอยู่ในขณะนี้จะเข้าคิวเพื่อดำเนินการ คลาสบริบทที่ใช้ในโปรแกรม Windows Forms จะจัดคิวโดยใช้คิวที่วนรอบข้อความถูกปั๊ม
ดังนั้นจึงกลับไปที่ลูปข้อความซึ่งตอนนี้สามารถปั๊มข้อความต่อไปได้เช่นย้ายหน้าต่างปรับขนาดหรือคลิกปุ่มอื่น
สำหรับผู้ใช้ UI จะตอบสนองอีกครั้งโดยประมวลผลการคลิกปุ่มอื่น ๆ การปรับขนาดและที่สำคัญที่สุดคือการวาดใหม่ดังนั้นจึงไม่ปรากฏว่าหยุดการทำงาน
- 2 วินาทีต่อมาสิ่งที่เรากำลังรอให้เสร็จสมบูรณ์และสิ่งที่เกิดขึ้นตอนนี้ก็คือ (บริบทการซิงโครไนซ์) วางข้อความลงในคิวที่ลูปข้อความกำลังดูโดยพูดว่า "เฮ้ฉันได้รับรหัสเพิ่มเติมอีก คุณเรียกใช้งาน "และรหัสนี้เป็นรหัสทั้งหมดหลังจากการรอคอย
- เมื่อวนรอบข้อความได้รับข้อความนั้นโดยทั่วไปแล้วจะ "ป้อนใหม่" วิธีการที่มันทิ้งไว้หลังจากนั้น
await
และดำเนินการส่วนที่เหลือของวิธีการต่อไป โปรดทราบว่ารหัสนี้ถูกเรียกอีกครั้งจากการวนรอบข้อความดังนั้นหากรหัสนี้เกิดขึ้นเพื่อทำบางสิ่งที่มีความยาวโดยไม่ต้องใช้async/await
อย่างถูกต้องมันจะบล็อกการวนรอบข้อความอีกครั้ง
มีชิ้นส่วนที่เคลื่อนไหวอยู่ใต้กระโปรงหน้ารถนี่คือบางส่วนที่เชื่อมโยงไปยังข้อมูลเพิ่มเติมฉันจะบอกว่า "คุณต้องการมัน" แต่หัวข้อนี้ค่อนข้างกว้างและมันค่อนข้างสำคัญที่จะต้องรู้ว่าชิ้นส่วนที่เคลื่อนไหวเหล่านั้นเป็นอย่างไร อย่างสม่ำเสมอคุณจะเข้าใจว่า async / await ยังคงเป็นแนวคิดรั่วไหล ข้อ จำกัด และปัญหาพื้นฐานบางประการยังคงรั่วไหลไปยังโค้ดที่อยู่รอบ ๆ และหากไม่เป็นเช่นนั้นคุณมักจะต้องตรวจแก้จุดบกพร่องของแอปพลิเคชันที่แตกตัวแบบสุ่มโดยไม่มีเหตุผลที่ดี
ตกลงดังนั้นจะเกิดอะไรGetSomethingAsync
ขึ้นถ้าหมุนเธรดที่จะเสร็จสมบูรณ์ภายใน 2 วินาที ใช่แล้วเห็นได้ชัดว่ามีหัวข้อใหม่ในการเล่น อย่างไรก็ตามเธรดนี้ไม่ได้เป็นเพราะ async-ness ของวิธีนี้เป็นเพราะโปรแกรมเมอร์ของวิธีนี้เลือกเธรดที่จะใช้โค้ดอะซิงโครนัส I / O อะซิงโครนัสเกือบทั้งหมดไม่ใช้เธรดพวกเขาใช้สิ่งต่าง ๆ async/await
ด้วยตัวเองไม่หมุนกระทู้ใหม่ แต่แน่นอนว่า "สิ่งที่เรารอ" อาจถูกนำไปใช้โดยใช้เธรด
มีหลายสิ่งใน. NET ที่ไม่จำเป็นต้องหมุนเธรดด้วยตนเอง แต่ยังคงไม่พร้อมกัน:
- คำขอทางเว็บ (และสิ่งอื่น ๆ ที่เกี่ยวข้องกับเครือข่ายที่ต้องใช้เวลา)
- การอ่านและการเขียนไฟล์แบบอะซิงโครนัส
- และอีกมากมายสัญญาณที่ดีคือถ้าคลาส / อินเทอร์เฟซที่สงสัยมีวิธีการตั้งชื่อ
SomethingSomethingAsync
หรือBeginSomething
และEndSomething
และมีIAsyncResult
ส่วนเกี่ยวข้อง
สิ่งเหล่านี้มักจะไม่ใช้ด้ายใต้กระโปรงหน้ารถ
ตกลงดังนั้นคุณต้องการบางสิ่งที่ "หัวข้อหัวข้อกว้าง"?
ทีนี้ลองถามโรสลินเกี่ยวกับการคลิกปุ่มของเรา:
ลองโรสลิน
ฉันจะไม่เชื่อมโยงในชั้นเรียนที่สร้างขึ้นเต็มรูปแบบที่นี่ แต่เป็นสิ่งที่เต็มไปด้วยเลือด