ฉันควรหลีกเลี่ยงตัวจัดการเหตุการณ์ 'async void' หรือไม่


119

ฉันรู้ว่าโดยทั่วไปถือว่าเป็นความคิดที่ไม่ดีที่จะใช้วิธีการจุดไฟและลืมasync voidเพื่อเริ่มงานเนื่องจากไม่มีงานที่รอดำเนินการอยู่และเป็นเรื่องยากที่จะจัดการข้อยกเว้นที่อาจเกิดขึ้นในวิธีการดังกล่าว

โดยทั่วไปฉันควรหลีกเลี่ยงasync voidตัวจัดการเหตุการณ์ด้วยหรือไม่ ตัวอย่างเช่น,

private async void Form_Load(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

ฉันสามารถเขียนใหม่ได้ดังนี้:

Task onFormLoadTask = null; // track the task, can implement cancellation

private void Form_Load(object sender, System.EventArgs e)
{
        this.onFormLoadTask = OnFormLoadTaskAsync(sender, e);
} 

private async Task OnFormLoadTaskAsync(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

หินใต้น้ำสำหรับตัวจัดการเหตุการณ์ async มีอะไรบ้างนอกเหนือจาก re-entrancy ที่เป็นไปได้?


คุณควร แต่ทำไม่ได้ นอกจากนั้นความใส่ใจทั้งหมดที่คุณต้องทำเมื่อใช้ async void นั้นเป็นสิ่งที่ผู้จัดการเหตุการณ์ UI ต้องการอยู่แล้ว
Paulo Morgado

และ reentrancy เกิดขึ้นเนื่องจากการดำเนินการแบบอะซิงโครนัสที่ยิงโดยตัวจัดการเหตุการณ์ไม่ใช่โดยการใช้ async-await ด้วยตัวเอง
Paulo Morgado

คำตอบ:


153

แนวทางคือหลีกเลี่ยงasync void ยกเว้นเมื่อใช้ในตัวจัดการเหตุการณ์ดังนั้นการใช้async voidในตัวจัดการเหตุการณ์จึงใช้ได้

ที่กล่าวว่าด้วยเหตุผลในการทดสอบหน่วยฉันมักชอบแยกตรรกะของasync voidวิธีการทั้งหมดออก เช่น,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
  await Task.Delay(2000);
  ...
}

private async void Form_Load(object sender, EventArgs e)
{
  await OnFormLoadAsync(sender, e);
}

ฉันสงสัย ... มีเหตุผลอะไรที่คุณไม่เพียงแค่เปลี่ยนForm_Loadสิทธิ์การเข้าถึงpublic? ดูเหมือนว่าโค้ดจะมีรายละเอียดน้อยกว่าด้วยวิธีนี้
InteXX

อ๊ะไม่เป็นไร ... VBer พยายามอ่าน C # ที่นี่ ... ฉันเพิ่งสังเกตประเภทการส่งคืนของOnFormLoadAsync. ฉันเห็นแล้วว่านี่เป็นเคล็ดลับที่มีประโยชน์ ขอบคุณ
InteXX

ทั้งหมดที่กล่าวว่าคุณอาจมีรูปลักษณ์และเสนอความเห็นที่นี่ ขอบคุณ!
InteXX

2
@ AlexHopeO'Connor ที่: Handledธงจะต้องมีการตั้งค่าพร้อมกัน; ไม่สามารถใช้asyncในการตัดสินใจว่าจะจัดการเหตุการณ์ได้หรือไม่
Stephen Cleary

2
@ AlexHopeO'Connor: เป็นเวลานานแล้วที่ฉันทำงานกับแอพ WPF แต่ฉันเคยใช้โซลูชันที่คล้ายกับที่ผ่านมา คือสร้างICommand.Executeวิธีการasync void; ฉันถือว่าสิ่งนี้ยอมรับได้เนื่องจากICommand.Executeเป็นตัวจัดการเหตุการณ์อย่างมีเหตุผล
Stephen Cleary

50

โดยทั่วไปฉันควรหลีกเลี่ยงตัวจัดการเหตุการณ์ที่เป็นโมฆะ async ด้วยหรือไม่

โดยทั่วไปตัวจัดการเหตุการณ์เป็นกรณีหนึ่งที่วิธี void async ไม่ใช่กลิ่นโค้ดที่เป็นไปได้

ตอนนี้หากคุณจำเป็นต้องติดตามงานด้วยเหตุผลบางประการเทคนิคที่คุณอธิบายนั้นสมเหตุสมผลอย่างยิ่ง


6

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

The only case where this kind of fire-and-forget is appropriate is in top-level event-handlers. Every other async method in your code should return "async Task".

นี่คือลิงค์


'ตัวจัดการเหตุการณ์ระดับบนสุด ' เป็นคำใบ้ที่สำคัญ เมื่อใช้ตัวจัดการเหตุการณ์ async void บนตัวจัดการเหตุการณ์ระดับล่างอาจทำให้เกิดปัญหาใหญ่โดยไม่พบข้อยกเว้น
Portikus

ขอบคุณสำหรับลิงค์วิดีโอมีประโยชน์มาก
lsp

5

หากคุณใช้ ReSharper ReCommended Extensionฟรีอาจเป็นประโยชน์สำหรับคุณ จะวิเคราะห์วิธีการ "async void" และไฮไลต์เมื่อใช้อย่างไม่เหมาะสม นามสกุลสามารถแยกแยะความแตกต่างการใช้งานที่แตกต่างกันของ async เป็นโมฆะและให้เหมาะสมกับการแก้ไขอย่างรวดเร็วอธิบายไว้ที่นี่: ขอแนะนำ-ขยายวิกิพีเดีย

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