นี่คือลำดับของข้อมูลโค้ดที่ฉันเพิ่งใช้เพื่อแสดงความแตกต่างและปัญหาต่างๆโดยใช้การแก้ปัญหาแบบ async
สมมติว่าคุณมีตัวจัดการเหตุการณ์บางอย่างในแอปพลิเคชันที่ใช้ GUI ซึ่งใช้เวลามากและคุณต้องการทำให้เป็นแบบอะซิงโครนัส นี่คือตรรกะแบบซิงโครนัสที่คุณเริ่มต้นด้วย:
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem ส่งคืนงานซึ่งในที่สุดจะให้ผลลัพธ์บางอย่างที่คุณต้องการตรวจสอบ หากผลลัพธ์ปัจจุบันเป็นผลลัพธ์ที่คุณต้องการคุณจะอัปเดตค่าของตัวนับบางตัวบน UI และส่งกลับจากวิธีการ มิฉะนั้นคุณยังคงประมวลผลรายการเพิ่มเติมจาก LoadNextItem
แนวคิดแรกสำหรับเวอร์ชันอะซิงโครนัส: ใช้การต่อเนื่อง! และเราจะไม่สนใจส่วนที่วนซ้ำในขณะนี้ ฉันหมายความว่ามีอะไรผิดพลาดบ้าง?
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
เยี่ยมมากตอนนี้เรามีวิธีที่ไม่ปิดกั้น! มันขัดข้องแทน การอัปเดตการควบคุม UI ควรเกิดขึ้นในเธรด UI ดังนั้นคุณจะต้องพิจารณาถึงสิ่งนั้น โชคดีที่มีตัวเลือกในการระบุวิธีกำหนดเวลาความต่อเนื่องและมีค่าเริ่มต้นสำหรับสิ่งนี้:
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
เยี่ยมมากตอนนี้เรามีวิธีที่ไม่ผิดพลาด! มันล้มเหลวอย่างเงียบ ๆ แทน ความต่อเนื่องเป็นงานที่แยกจากกันโดยที่สถานะของพวกเขาไม่ได้เชื่อมโยงกับภารกิจก่อนหน้า ดังนั้นแม้ว่า LoadNextItem จะเกิดข้อผิดพลาดผู้เรียกจะเห็นเฉพาะงานที่ทำสำเร็จแล้ว โอเคจากนั้นส่งต่อข้อยกเว้นหากมี:
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
เยี่ยมมากตอนนี้ใช้งานได้จริง สำหรับรายการเดียว ทีนี้การวนลูปนั้นเป็นอย่างไร ปรากฎว่าโซลูชันที่เทียบเท่ากับตรรกะของเวอร์ชันซิงโครนัสดั้งเดิมจะมีลักษณะดังนี้:
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
หรือแทนที่จะใช้ทั้งหมดข้างต้นคุณสามารถใช้ async เพื่อทำสิ่งเดียวกัน:
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
ตอนนี้ดีกว่ามากแล้วใช่ไหม
Wait
การโทรในตัวอย่างที่สองแล้วทั้งสองจะเป็นตัวอย่าง (ส่วนใหญ่) เทียบเท่า