ก่อนอื่นขอขอบคุณสำหรับคำพูดที่ดีของคุณ มันเป็นคุณสมบัติที่ยอดเยี่ยมจริงๆและฉันดีใจที่ได้เป็นส่วนเล็ก ๆ ของมัน
หากรหัสทั้งหมดของฉันเปลี่ยนเป็น async อย่างช้าๆทำไมไม่ทำให้ async ทั้งหมดเป็นค่าเริ่มต้น
คุณพูดเกินจริง รหัสทั้งหมดของคุณไม่เปลี่ยนเป็น async เมื่อคุณบวกจำนวนเต็ม "ธรรมดา" สองตัวเข้าด้วยกันคุณจะไม่รอผลลัพธ์ เมื่อคุณบวกจำนวนเต็มในอนาคตสองจำนวนเข้าด้วยกันเพื่อให้ได้จำนวนเต็มในอนาคตที่สามเพราะนั่นTask<int>
คือจำนวนเต็มที่คุณจะสามารถเข้าถึงได้ในอนาคตแน่นอนว่าคุณอาจกำลังรอผลลัพธ์อยู่
เหตุผลหลักที่ไม่ทำให้ทุกอย่าง async เป็นเพราะจุดประสงค์ของ async / await คือทำให้ง่ายต่อการเขียนโค้ดในโลกที่มีการดำเนินการแฝงสูงมากมาย การดำเนินการส่วนใหญ่ของคุณไม่ใช่เวลาในการตอบสนองที่สูงดังนั้นจึงไม่สมเหตุสมผลที่จะใช้ผลการดำเนินงานที่ช่วยลดความหน่วงนั้น แต่การดำเนินการที่สำคัญบางประการของคุณมีความหน่วงแฝงสูงและการดำเนินการเหล่านั้นทำให้ซอมบี้แพร่ระบาดของ async ตลอดทั้งรหัส
หากประสิทธิภาพเป็นปัญหาเดียวแน่นอนว่าการเพิ่มประสิทธิภาพที่ชาญฉลาดบางอย่างสามารถลบค่าโสหุ้ยได้โดยอัตโนมัติเมื่อไม่จำเป็น
ในทางทฤษฎีทฤษฎีและการปฏิบัติมีความคล้ายคลึงกัน ในทางปฏิบัติพวกเขาไม่เคยเป็น
ผมขอให้สามคะแนนกับการเปลี่ยนแปลงประเภทนี้ตามด้วยการเพิ่มประสิทธิภาพ
จุดแรกอีกครั้งคือ async ใน C # / VB / F # โดยพื้นฐานแล้วเป็นการส่งผ่านความต่อเนื่องที่จำกัด งานวิจัยจำนวนมหาศาลในชุมชนภาษาที่ใช้งานได้ได้ค้นหาวิธีการระบุวิธีเพิ่มประสิทธิภาพโค้ดที่ใช้รูปแบบการส่งผ่านความต่อเนื่องอย่างหนัก ทีมคอมไพเลอร์น่าจะต้องแก้ปัญหาที่คล้ายกันมากในโลกที่ "async" เป็นค่าเริ่มต้นและต้องระบุวิธีการที่ไม่ใช่ async และ de-async-ified ทีม C # ไม่สนใจที่จะแก้ปัญหาการวิจัยแบบเปิดดังนั้นจึงเป็นประเด็นสำคัญที่ตรงนั้น
ประเด็นที่สองคือ C # ไม่มีระดับ "ความโปร่งใสในการอ้างอิง" ที่ทำให้การเพิ่มประสิทธิภาพประเภทนี้สามารถดึงข้อมูลได้มากขึ้น โดย "อ้างอิงความโปร่งใส" ผมหมายถึงทรัพย์สินที่มูลค่าของการแสดงออกไม่ได้ขึ้นอยู่กับเมื่อมันได้รับการประเมิน การแสดงออกเช่น2 + 2
มีความโปร่งใสอ้างอิง คุณสามารถทำการประเมินผลในเวลาคอมไพล์ได้หากต้องการหรือเลื่อนออกไปจนกว่าจะรันไทม์และได้รับคำตอบเดียวกัน แต่การแสดงออกเช่นx+y
ไม่สามารถย้ายไปรอบ ๆ ในเวลาเพราะx และ y อาจจะมีการเปลี่ยนแปลงอยู่ตลอดเวลา
Async ทำให้ยากขึ้นมากที่จะหาเหตุผลว่าผลข้างเคียงจะเกิดขึ้นเมื่อใด ก่อน async ถ้าคุณพูดว่า:
M();
N();
และM()
เป็นvoid M() { Q(); R(); }
และN()
เป็นvoid N() { S(); T(); }
และR
และS
ก่อให้เกิดผลข้างเคียงคุณจะรู้ว่าผลข้างเคียงของ R เกิดขึ้นก่อนผลข้างเคียงของ S แต่ถ้าคุณมีasync void M() { await Q(); R(); }
แล้วจู่ๆก็ออกไปนอกหน้าต่าง คุณไม่สามารถรับประกันได้ว่าR()
จะเกิดขึ้นก่อนหรือหลังS()
(เว้นแต่M()
จะมีการรอคอยแน่นอน แต่แน่นอนว่าTask
ไม่จำเป็นต้องรอจนกว่าจะถึงภายหลังN()
)
ตอนนี้คิดว่าทรัพย์สินนี้ไม่ทราบว่าสิ่งที่ผลข้างเคียงที่เกิดขึ้นในการสั่งซื้อนำไปใช้กับชิ้นส่วนของรหัสในโปรแกรมของคุณทุกคนยกเว้นผู้ที่เพิ่มประสิทธิภาพการบริหารจัดการที่จะยกเลิกการ async-Ify โดยพื้นฐานแล้วคุณไม่มีเงื่อนงำอีกต่อไปว่านิพจน์ใดจะได้รับการประเมินตามลำดับซึ่งหมายความว่านิพจน์ทั้งหมดจะต้องมีความโปร่งใสในการอ้างอิงซึ่งยากในภาษาเช่น C #
ประเด็นที่สามคือคุณต้องถามว่า "ทำไม async ถึงพิเศษ?" หากคุณจะโต้แย้งว่าการดำเนินการทุกอย่างควรเป็นจริงTask<T>
คุณต้องสามารถตอบคำถามได้ว่า "ทำไมไม่Lazy<T>
" หรือ "ทำไมไม่Nullable<T>
" หรือ "ทำไมไม่IEnumerable<T>
" เพราะเราสามารถทำได้อย่างง่ายดาย ทำไมมันไม่ควรจะเป็นกรณีที่การดำเนินงานทุกคนจะยก nullable ? หรือการดำเนินการทุกอย่างเฉื่อยชาคำนวณและผลที่ได้ถูกแคชในภายหลังหรือผลการดำเนินงานทุกคนเป็นลำดับของค่าแทนเพียงค่าเดียว จากนั้นคุณต้องพยายามเพิ่มประสิทธิภาพสถานการณ์เหล่านั้นโดยที่คุณรู้ว่า "โอ้นี่จะต้องไม่เป็นโมฆะฉันจึงจะสร้างโค้ดที่ดีขึ้นได้" และอื่น ๆ
ประเด็นคือ: มันไม่ชัดเจนสำหรับฉันที่Task<T>
จริง ๆ แล้วมันพิเศษที่จะรับประกันงานนี้มาก
หากสิ่งเหล่านี้ทำให้คุณสนใจฉันขอแนะนำให้คุณตรวจสอบภาษาที่ใช้งานได้เช่น Haskell ซึ่งมีความโปร่งใสในการอ้างอิงที่ดีกว่ามากและอนุญาตให้มีการประเมินผลนอกลำดับทุกประเภทและทำการแคชอัตโนมัติ Haskell ยังมีการสนับสนุนที่แข็งแกร่งกว่ามากในระบบประเภทของ "การยกเสาเดียว" ที่ฉันพูดถึง