การผสมวิธีการซิงค์และ async มีประสิทธิภาพภายในวิธีการเดียวหรือไม่


11

โอเคฟังดูแปลก แต่รหัสนั้นง่ายมากและอธิบายสถานการณ์ได้ดี

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

(ฉันรู้ว่าฉันกำลังพูดถึงในบทคัดย่อด้านล่าง แต่ข้างต้นเป็นวิธีการที่เกิดขึ้นจริงจากสิ่งที่จะเป็นรหัสการผลิตจริงที่ทำในสิ่งที่ฉันถามเกี่ยวกับที่นี่และฉันสนใจในการตรวจสอบจริงของคุณ เพื่อความถูกต้องโปรดดูรูปแบบ async / await)

ฉันพบรูปแบบนี้บ่อยขึ้นเรื่อย ๆ ในขณะที่ฉันกำลังใช้async/ awaitมากกว่า รูปแบบประกอบด้วยห่วงโซ่ของเหตุการณ์ต่อไปนี้:

  1. รอสายแรกที่ทำให้ฉันได้รับข้อมูลบางอย่างที่ฉันต้องทำ
  2. ทำงานกับข้อมูลนั้นพร้อมกัน
  3. รอสายสุดท้ายที่บันทึกงานที่อัปเดต

โดยทั่วไปโค้ดบล็อกด้านบนเป็นวิธีที่ฉันใช้จัดการวิธีการเหล่านี้ ฉันawaitโทรครั้งแรกซึ่งฉันต้องทำเพราะมันเป็นแบบอะซิงโครนัส ต่อไปฉันทำงานที่ฉันต้องทำซึ่งไม่ใช่ IO หรือทรัพยากรถูกผูกไว้และไม่ใช่ async สุดท้ายผมบันทึกการทำงานของฉันซึ่งยังเป็นผู้asyncโทรเข้าและออกของสินค้าลัทธิผมawaitมัน

แต่นี่เป็นวิธีที่มีประสิทธิภาพ / ถูกที่สุดในการจัดการรูปแบบนี้หรือไม่? ดูเหมือนว่าฉันจะสามารถข้ามawaitสายสุดท้ายได้ แต่จะเกิดอะไรขึ้นถ้ามันล้มเหลว และฉันควรใช้Taskวิธีการเช่นContinueWithทำงานสายซิงโครนัสกับสายเดิมหรือไม่ ฉันเพิ่งมาถึงจุดที่ฉันไม่แน่ใจว่าฉันจัดการอย่างถูกต้องหรือไม่

ให้รหัสในตัวอย่างมีวิธีที่ดีกว่าในการจัดการห่วงโซ่การเรียกวิธีนี้ async / sync / async?


รหัสดูเหมือนว่าฉันสั้นและเข้าใจได้มากที่สุด ทุกสิ่งที่ฉันสามารถนึกถึงแนะนำความซับซ้อนที่ไม่จำเป็น
ร่าเริง

@Eparhoric: ฉันควรจะรอสายสุดท้ายของฉันหรือไม่ จะเกิดอะไรขึ้นถ้าฉันทำไม่ได้ มันจะแตกต่างจากที่เป็นอยู่ในปัจจุบันหรือไม่?
ฉีก

คำตอบ:


3

ใช่ฉันคิดว่านี่เป็นวิธีที่ถูกต้องที่จะทำ

คุณไม่สามารถข้ามวินาทีawaitได้ หากคุณทำเช่นนั้นวิธีการดังกล่าวจะเสร็จเร็วเกินไป (ก่อนที่จะทำการลบออกจริง) และคุณจะไม่มีทางรู้ว่าการลบนั้นล้มเหลวหรือไม่

ฉันไม่เห็นว่าจะมีวิธีContinueWith()ใดหรืออะไรเช่นนั้นที่นี่ คุณสามารถใช้มันเพื่อหลีกเลี่ยงการใช้awaitแต่มันจะทำให้โค้ดของคุณซับซ้อนและอ่านได้น้อยลง และนั่นคือจุดรวมของawait: ทำให้การเขียนโค้ดแบบอะซิงโครนัสง่ายขึ้นเมื่อเปรียบเทียบกับการใช้การต่อเนื่อง


0

วิธีจัดการรูปแบบนี้คือเพื่อให้แน่ใจว่า I / O ทั้งหมดไม่ตรงกัน วิธีการ I / O แบบซิงโครนัสทำให้เธรดปัจจุบันบล็อกในขณะที่รอการตอบกลับจากปลายทาง I / O (เครือข่ายระบบไฟล์ ฯลฯ )

อีกสิ่งที่awaitควรพิจารณาคือควรใช้เมื่อคุณต้องการส่งคืนค่าหรือเมื่อคุณต้องการรหัสที่รอคอยให้เสร็จก่อนที่จะทำอย่างอื่น หากคุณไม่ต้องการอย่างใดอย่างหนึ่งในสิ่งที่คุณสามารถ "ไฟและลืม" วิธีการ async Task.Runของคุณด้วย

ดังนั้นเพื่อการใช้งานทรัพยากรคอมพิวเตอร์อย่างมีประสิทธิภาพสูงสุดหากRemoveRolesทำ I / O ใด ๆ มันควรจะกลายเป็นawait RemoveRolesAsyncและวิธีการ I / O ที่เรียกโดยRemoveRolesAsyncควรเป็น async (และอาจรอ)

หากประสิทธิภาพไม่ใช่ความกังวลสูงสุดของคุณก็เป็นเรื่องดีที่จะทำ I / O แบบซิงโครนัสบนเธรด async มันเป็นหนี้ทางเทคนิคแม้ว่า (ในกรณีนี้คุณอาจต้องการเรียกวิธีการ async แรกด้วยConfigureAwaitโดยขึ้นอยู่กับว่าโค้ดกำลังทำงานอยู่ที่ใด)

ต่อไปนี้เป็นมุมมองเชิงลึกเพิ่มเติมเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุด - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

นี่คือหมายเหตุบางประการเกี่ยวกับพฤติกรรมของ ConfigureAwait ในสภาพแวดล้อมที่แตกต่างกันเช่น ASP.NET, WebAPI และอื่น ๆ - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -รหัส


2
คุณไม่ควรดำเนินการและลืมรหัสด้วย Task.Run งานทั้งหมดควรจะรอ หากคุณไม่รองานและภารกิจถูกรวบรวมขยะในสถานะที่ข้อยกเว้นคือ "ไม่ได้รับการตรวจสอบ" จะทำให้ UnobservedTaskException เกิดขึ้นในรันไทม์และอาจทำให้แอปพลิเคชันของคุณทำงานผิดพลาดขึ้นอยู่กับรุ่นของเฟรมเวิร์ก
Triynko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.