ปัญหาคือคุณใช้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นั้นคุณควรประกาศตัวแปรเป็นชนิดmyTaskTask<Task<string>>
หมายเหตุ:ฉันไม่รับรองการใช้ตัวTaskสร้างสำหรับการสร้างงานเย็น โดยทั่วไปแล้วการทำหน้านิ่วคิ้วขมวดด้วยเหตุผลที่ฉันไม่รู้จริง ๆ แต่อาจเป็นเพราะมีการใช้บ่อยครั้งจนมีโอกาสจับผู้ใช้ / ผู้ดูแล / ผู้ตรวจทานคนอื่น ๆ ที่ไม่รู้ตัวของโค้ดด้วยความประหลาดใจ
คำแนะนำทั่วไป:ระวังทุกครั้งที่คุณส่งตัวแทน async ให้เป็นวิธีการ วิธีนี้ควรคาดว่าจะFunc<Task>โต้แย้ง (หมายถึงเข้าใจตัวแทน async) หรืออย่างน้อยFunc<T>อาร์กิวเมนต์ (หมายความว่าอย่างน้อยสร้างTaskจะไม่ถูกละเว้น) ในกรณีที่โชคร้ายที่วิธีการนี้ยอมรับตัวแทนของคุณเป็นไปที่จะถือว่าเป็นAction async voidนี่เป็นสิ่งที่คุณต้องการไม่บ่อยนัก