async รอพฤติกรรม [ซ้ำ]
ฉันกำลังทดลองกับ async-await และฉันก็เจอพฤติกรรมแปลก ๆ นี้อย่างน้อยก็สำหรับฉัน
ฉันสร้างวิธีการสามวิธีที่จำลองงานที่ต้องใช้เวลานาน
พิจารณาตัวจัดการการคลิกปุ่มสองปุ่ม:
สำหรับ button1_click เวลาที่ผ่านไปอยู่ที่ประมาณ 6000ms ในขณะที่ button2_click ประมาณ 3000ms
ฉันไม่สามารถคาดเดาได้ว่าทำไมสิ่งนี้ถึงเกิดขึ้นเช่น 6000ms กับ 3000ms
private async Task<string> TaskOne()
{
await Task.Delay(1000);
return "task one";
}
private async Task<string> TaskTwo()
{
await Task.Delay(2000);
return "task two";
}
private async Task<string> TaskThree()
{
await Task.Delay(3000);
return "task three";
}
//time elapsed = 6000+ms
private async void button1_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
await TaskOne();
await TaskTwo();
await TaskThree();
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
//time elapsed = 3000+ms
private async void button2_Click(object sender, EventArgs e)
{
var watch = new Stopwatch();
watch.Start();
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
watch.Stop();
textBox3.Text = watch.ElapsedMilliseconds.ToString();
}
คำตอบ
ในกรณีนี้:
await TaskOne();
await TaskTwo();
await TaskThree();
TaskTwo () ไม่สามารถเริ่มทำงานได้จนกว่า TaskOne () จะเสร็จสมบูรณ์เนื่องจากคุณกำลังรออยู่ ในทำนองเดียวกัน TaskThree () ไม่สามารถเริ่มทำงานได้จนกว่า TaskTwo () จะเสร็จสมบูรณ์เนื่องจากการรอ
ในครั้งต่อไป:
var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();
await taskOne;
await taskTwo;
await taskThree;
คุณกำลังเริ่มงานทั้ง 3 อย่างในเวลาเดียวกันจากนั้นรองานเหล่านั้น นั่นเป็นเหตุผลว่าทำไมจึงใช้เวลาเพียงแค่เป็นงานที่ทำงานนานที่สุด คุณจะแปลกใจที่มีคนจำนวนมากไม่เข้าใจเรื่องนี้เกี่ยวกับ async ที่รอคอย หากงานไม่ได้ขึ้นอยู่กับกันและกันนี่คือหนทางที่จะไป
สรุป
จุดรับกลับบ้านที่นี่ (และเป็นความเข้าใจผิดที่พบบ่อยมากเป็นอย่างอื่น) นั่นawaitหมายความว่า"รอ"จริงๆ
รอตัวดำเนินการ (อ้างอิง C #)
เน้นของฉัน
การประเมินผลผู้ประกอบการรอคอยระงับของวิธี async ล้อมรอบจนกว่าการดำเนินการไม่ตรงกันแสดงโดยเสร็จสมบูรณ์ถูกดำเนินการของ เมื่อการดำเนินการแบบอะซิงโครนัสเสร็จสิ้นตัวดำเนินการ await จะส่งคืนผลลัพธ์ของการดำเนินการถ้ามี
เมื่อผู้ประกอบการรอคอยนำมาใช้กับตัวถูกดำเนินการที่แสดงถึงการดำเนินการเสร็จสิ้นแล้วก็จะส่งกลับผลการดำเนินงานได้ทันทีโดยไม่ต้องหยุดชะงักของวิธีการปิดล้อม
ตัวดำเนินการ await ไม่บล็อกเธรดที่ประเมินเมธอด async เมื่อตัวดำเนินการ await ระงับเมธอด async ที่แนบมาตัวควบคุมจะส่งกลับไปยังผู้เรียกของเมธอด
นี่คือสิ่งที่เกิดขึ้น ในตัวอย่างแรกของคุณคุณกำลังเริ่มงานแต่ละอย่างและรอให้เสร็จอย่างต่อเนื่อง กล่าวคือเหมือนกับการขอให้ใครบางคนไปทำอะไรบางอย่างและทำอะไรให้เสร็จก่อนที่จะขอให้คนต่อไปทำอะไรบางอย่าง ฯลฯ
await TaskOne(); // start, do something and wait for it
await TaskTwo(); // start, do something and wait for it
await TaskThree(); // start, do something and wait for it
ตัวอย่างที่สองของคุณ คุณกำลังเริ่มงาน 3 อย่าง (ร้อนแรง) เป็นหลักจากนั้นรอให้เสร็จทีละอย่าง กล่าวคือพวกเขาทำงานพร้อมกันและรอคอยตามลำดับ
คือคุณพูดกับเพื่อน 3 คนไปทำของแล้วรอคนแรกกลับมาแล้วคนที่สองก็คนที่สาม มีประสิทธิภาพกว่ามาก ... ไม่มีเพื่อนที่น่ารำคาญอยู่ใกล้ ๆ จนกว่าคนก่อนหน้าจะกลับมา
แม้ว่างานที่สองจะเสร็จสิ้นก่อนงานแรกคุณยังคงมีผลรองานแรกก่อนที่จะดูสถานะที่เสร็จสมบูรณ์ของงานที่สองเป็นต้น
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// all 3 tasks are started
await taskOne; // wait for it
await taskTwo; // wait for it
await taskThree; // wait for it
หรือในทำนองเดียวกันคุณสามารถใช้ Task.WhenAll
สร้างงานที่จะเสร็จสมบูรณ์เมื่องานที่ให้มาทั้งหมดเสร็จสิ้น
var taskOne = TaskOne(); // start, do something
var taskTwo = TaskTwo(); // start, do something
var taskThree = TaskThree(); // start, do something
// wait for them all to finish!
await Task.WhenAll(taskOne, taskTwo, taskThree);