ปัญหาคือคุณใช้Task
คลาสที่ไม่ใช่แบบทั่วไปซึ่งไม่ได้หมายถึงการให้ผลลัพธ์ ดังนั้นเมื่อคุณสร้างTask
อินสแตนซ์ผ่านตัวแทน async:
Task myTask = new Task(async () =>
... async void
ผู้ร่วมประชุมจะถือว่าเป็น async void
ไม่ได้เป็นTask
ก็ไม่สามารถรอคอยข้อยกเว้นที่ไม่สามารถจัดการได้และเป็นแหล่งที่มาของพันของคำถามที่ทำโดยโปรแกรมเมอร์ผิดหวังที่นี่ใน StackOverflow และที่อื่น ๆ การแก้ปัญหาคือการใช้งานทั่วไปชั้นเพราะคุณต้องการที่จะกลับผลและผลที่ได้ก็เป็นอีกหนึ่งTask<TResult>
Task
ดังนั้นคุณต้องสร้างTask<Task>
:
Task<Task> myTask = new Task<Task>(async () =>
ตอนนี้เมื่อคุณStart
ด้านนอกก็จะแล้วเสร็จเกือบจะทันทีเพราะงานของตนเป็นเพียงการสร้างภายในTask<Task>
Task
จากนั้นคุณจะต้องรอด้านในTask
เช่นกัน นี่คือวิธีที่สามารถทำได้:
myTask.Start();
Task myInnerTask = await myTask;
await myInnerTask;
คุณมีสองทางเลือก หากคุณไม่ต้องการการอ้างอิงที่ชัดเจนไปยังด้านในTask
คุณสามารถรอด้านนอกTask<Task>
สองครั้ง:
await await myTask;
... หรือคุณสามารถใช้วิธีการขยายในตัวUnwrap
ที่รวมงานด้านนอกและงานด้านในเป็นหนึ่งเดียว:
await myTask.Unwrap();
การคลายออกจะเกิดขึ้นโดยอัตโนมัติเมื่อคุณใช้Task.Run
วิธีการยอดนิยมที่สร้างงานร้อนแรงขึ้นดังนั้นจึงUnwrap
ไม่ได้มีการใช้บ่อยมากในปัจจุบัน
ในกรณีที่คุณตัดสินใจว่าผู้ร่วมประชุม async ของคุณจะต้องกลับมาส่งผลให้เช่นstring
นั้นคุณควรประกาศตัวแปรเป็นชนิดmyTask
Task<Task<string>>
หมายเหตุ:ฉันไม่รับรองการใช้ตัวTask
สร้างสำหรับการสร้างงานเย็น โดยทั่วไปแล้วการทำหน้านิ่วคิ้วขมวดด้วยเหตุผลที่ฉันไม่รู้จริง ๆ แต่อาจเป็นเพราะมีการใช้บ่อยครั้งจนมีโอกาสจับผู้ใช้ / ผู้ดูแล / ผู้ตรวจทานคนอื่น ๆ ที่ไม่รู้ตัวของโค้ดด้วยความประหลาดใจ
คำแนะนำทั่วไป:ระวังทุกครั้งที่คุณส่งตัวแทน async ให้เป็นวิธีการ วิธีนี้ควรคาดว่าจะFunc<Task>
โต้แย้ง (หมายถึงเข้าใจตัวแทน async) หรืออย่างน้อยFunc<T>
อาร์กิวเมนต์ (หมายความว่าอย่างน้อยสร้างTask
จะไม่ถูกละเว้น) ในกรณีที่โชคร้ายที่วิธีการนี้ยอมรับตัวแทนของคุณเป็นไปที่จะถือว่าเป็นAction
async void
นี่เป็นสิ่งที่คุณต้องการไม่บ่อยนัก