เมื่อวานนี้ฉันได้พูดคุยเกี่ยวกับคุณลักษณะใหม่ของ "async" C โดยเฉพาะอย่างยิ่งการตรวจสอบสิ่งที่สร้างรหัสดูเหมือนและthe GetAwaiter()
/ BeginAwait()
/ EndAwait()
โทร
เราดูรายละเอียดที่เครื่องสถานะที่สร้างโดยคอมไพเลอร์ C # และมีสองด้านที่เราไม่เข้าใจ:
- เหตุใดคลาสที่สร้างขึ้นจึงมี
Dispose()
เมธอดและ$__disposing
ตัวแปรซึ่งไม่เคยปรากฏที่จะใช้ (และคลาสไม่ได้นำไปใช้IDisposable
) - สาเหตุที่
state
ตัวแปรภายในถูกตั้งค่าเป็น 0 ก่อนการเรียกใด ๆEndAwait()
เมื่อโดยปกติ 0 ปรากฏว่าหมายถึง "นี่คือจุดเริ่มต้น"
ฉันสงสัยว่าจุดแรกสามารถตอบได้โดยทำสิ่งที่น่าสนใจมากขึ้นในวิธีการ async ถึงแม้ว่าหากใครมีข้อมูลเพิ่มเติมฉันก็ยินดีที่จะได้ยิน อย่างไรก็ตามคำถามนี้เกี่ยวกับประเด็นที่สองมากกว่า
นี่คือตัวอย่างโค้ดที่ง่ายมาก:
using System.Threading.Tasks;
class Test
{
static async Task<int> Sum(Task<int> t1, Task<int> t2)
{
return await t1 + await t2;
}
}
... และนี่คือรหัสที่ได้รับการสร้างขึ้นสำหรับMoveNext()
วิธีการที่ใช้เครื่องรัฐ สิ่งนี้ถูกคัดลอกโดยตรงจาก Reflector - ฉันยังไม่ได้แก้ไขชื่อตัวแปรที่ไม่สามารถบรรยายได้:
public void MoveNext()
{
try
{
this.$__doFinallyBodies = true;
switch (this.<>1__state)
{
case 1:
break;
case 2:
goto Label_00DA;
case -1:
return;
default:
this.<a1>t__$await2 = this.t1.GetAwaiter<int>();
this.<>1__state = 1;
this.$__doFinallyBodies = false;
if (this.<a1>t__$await2.BeginAwait(this.MoveNextDelegate))
{
return;
}
this.$__doFinallyBodies = true;
break;
}
this.<>1__state = 0;
this.<1>t__$await1 = this.<a1>t__$await2.EndAwait();
this.<a2>t__$await4 = this.t2.GetAwaiter<int>();
this.<>1__state = 2;
this.$__doFinallyBodies = false;
if (this.<a2>t__$await4.BeginAwait(this.MoveNextDelegate))
{
return;
}
this.$__doFinallyBodies = true;
Label_00DA:
this.<>1__state = 0;
this.<2>t__$await3 = this.<a2>t__$await4.EndAwait();
this.<>1__state = -1;
this.$builder.SetResult(this.<1>t__$await1 + this.<2>t__$await3);
}
catch (Exception exception)
{
this.<>1__state = -1;
this.$builder.SetException(exception);
}
}
มันยาว แต่บรรทัดสำคัญสำหรับคำถามนี้คือ:
// End of awaiting t1
this.<>1__state = 0;
this.<1>t__$await1 = this.<a1>t__$await2.EndAwait();
// End of awaiting t2
this.<>1__state = 0;
this.<2>t__$await3 = this.<a2>t__$await4.EndAwait();
ในทั้งสองกรณีสถานะจะเปลี่ยนอีกครั้งหลังจากนั้นก่อนที่จะสังเกตเห็นได้ชัดต่อไป ... ดังนั้นทำไมจึงตั้งเป็น 0 เลย หากMoveNext()
ถูกเรียกอีกครั้ง ณ จุดนี้ (ไม่ว่าโดยตรงหรือผ่านทางDispose
) มันก็จะเริ่มต้นวิธีการซิงค์ได้อย่างมีประสิทธิภาพอีกครั้งซึ่งจะไม่เหมาะสมอย่างเต็มที่เท่าที่ฉันสามารถบอกได้ว่า ... ถ้าMoveNext()
ไม่ได้ถูกเรียก
นี่เป็นเพียงผลข้างเคียงของคอมไพเลอร์ที่ใช้รหัสการสร้างบล็อกตัววนซ้ำสำหรับ async ซึ่งอาจมีคำอธิบายที่ชัดเจนกว่านี้หรือไม่
ข้อจำกัดความรับผิดชอบที่สำคัญ
เห็นได้ชัดว่านี่เป็นเพียงคอมไพเลอร์ CTP ฉันคาดหวังอย่างเต็มที่ว่าสิ่งต่าง ๆ จะเปลี่ยนแปลงก่อนที่จะปล่อยรุ่นสุดท้าย - และอาจเป็นไปได้ก่อนถึงรุ่น CTP ถัดไป คำถามนี้ไม่มีทางพยายามที่จะอ้างว่านี่เป็นข้อบกพร่องในคอมไพเลอร์ C # หรืออะไรทำนองนั้น ฉันแค่พยายามคิดออกว่ามีเหตุผลที่ลึกซึ้งสำหรับเรื่องนี้หรือเปล่าที่ฉันพลาด :)