ทำไมภาษาโปรแกรมไม่จัดการปัญหาซิงโครนัส / อะซิงโครนัสโดยอัตโนมัติ?


27

ฉันไม่ได้พบแหล่งข้อมูลมากมายเกี่ยวกับสิ่งนี้: ฉันสงสัยว่ามันเป็นไปได้ / เป็นความคิดที่ดีที่จะสามารถเขียนโค้ดแบบอะซิงโครนัสในแบบซิงโครนัส

ตัวอย่างเช่นนี่คือโค้ด JavaScript บางอย่างที่ดึงจำนวนผู้ใช้ที่เก็บในฐานข้อมูล (การดำเนินการแบบอะซิงโครนัส):

getNbOfUsers(function (nbOfUsers) { console.log(nbOfUsers) });

มันคงจะดีถ้าได้เขียนอะไรแบบนี้:

const nbOfUsers = getNbOfUsers();
console.log(getNbOfUsers);

console.logและเพื่อให้คอมไพเลอร์โดยอัตโนมัติจะดูแลการรอคอยสำหรับการตอบสนองแล้วรัน มันจะรอให้การดำเนินการแบบอะซิงโครนัสเสร็จสมบูรณ์ก่อนที่ผลลัพธ์จะต้องถูกนำไปใช้ที่อื่น เราจะใช้สัญญาการติดต่อกลับน้อยลงอย่างมาก async / รอหรืออะไรก็ตามและไม่ต้องกังวลว่าผลลัพธ์ของการดำเนินการจะพร้อมใช้งานทันทีหรือไม่

ข้อผิดพลาดจะยังคงสามารถจัดการได้ (ได้nbOfUsersรับจำนวนเต็มหรือข้อผิดพลาด?) โดยใช้ลอง / จับหรือสิ่งที่ชอบตัวเลือกเช่นในภาษาสวิฟท์

มันเป็นไปได้? มันอาจเป็นความคิดที่น่ากลัว / ยูโทเปีย ... ฉันไม่รู้


58
ฉันไม่เข้าใจคำถามของคุณ หากคุณ "รอการดำเนินการแบบอะซิงโครนัสเสมอ" นั่นไม่ใช่การดำเนินการแบบอะซิงโครนัสมันเป็นการดำเนินการแบบซิงโครนัส คุณช่วยอธิบายได้ไหม อาจให้รายละเอียดเกี่ยวกับประเภทของพฤติกรรมที่คุณกำลังมองหาใช่ไหม นอกจากนี้ "สิ่งที่คุณคิดเกี่ยวกับมัน" เป็นปิดหัวข้อเกี่ยวกับวิศวกรรมซอฟต์แวร์ คุณต้องกำหนดคำถามของคุณในบริบทของปัญหาที่เป็นรูปธรรมซึ่งมีคำตอบเดียวที่ไม่คลุมเครือยอมรับได้อย่างถูกต้อง
Jörg W Mittag

4
@ JörgWMittagฉันคิดสมมุติ C # ที่ปริยายawaitSA Task<T>เพื่อแปลงเป็นT
Caleth

6
สิ่งที่คุณเสนอไม่สามารถทำได้ มันไม่ได้แปลขึ้นอยู่กับการตัดสินใจว่าคุณต้องการรอผลหรืออาจจะยิงและลืม หรือทำงานในพื้นหลังและรอในภายหลัง ทำไมต้อง จำกัด ตัวเองเช่นนั้น
ประหลาด

5
ใช่มันเป็นความคิดที่แย่มาก เพียงใช้async/ awaitแทนซึ่งทำให้ส่วน async ของการประมวลผลชัดเจน
Bergi

5
เมื่อคุณพูดว่ามีสองสิ่งเกิดขึ้นพร้อมกันคุณกำลังบอกว่าไม่เป็นไรที่สิ่งเหล่านี้จะเกิดขึ้นในลำดับใด ๆ หากรหัสของคุณไม่มีวิธีทำให้ชัดเจนว่าการสั่งซื้อซ้ำจะไม่ทำลายความคาดหวังของรหัสคุณจะไม่สามารถทำให้พร้อมกันได้
Rob

คำตอบ:


65

Async / await นั้นเป็นการจัดการอัตโนมัติที่คุณเสนอแม้ว่าจะมีสองคำหลักเพิ่มเติม ทำไมพวกเขาถึงสำคัญ? นอกเหนือจากความเข้ากันได้ย้อนหลังหรือไม่

  • หากไม่มีจุดที่ชัดเจนที่ coroutine อาจถูกระงับและกลับมาทำงานต่อเราจะต้องมีระบบประเภทเพื่อตรวจสอบว่าจะต้องรอค่าที่รอคอยได้อย่างไร ภาษาการเขียนโปรแกรมจำนวนมากไม่มีระบบประเภทดังกล่าว

  • โดยการรอค่าที่ชัดเจนเราสามารถส่งผ่านค่าที่รอคอยเป็นวัตถุชั้นหนึ่ง: สัญญา สิ่งนี้มีประโยชน์มากเมื่อเขียนรหัสคำสั่งที่สูงกว่า

  • รหัส Async มีผลกระทบลึกมากสำหรับรูปแบบการดำเนินการของภาษาคล้ายกับการไม่มีหรือมีข้อยกเว้นในภาษา โดยเฉพาะอย่างยิ่งฟังก์ชั่น async สามารถรอได้โดยฟังก์ชั่น async สิ่งนี้มีผลต่อฟังก์ชั่นการโทรทั้งหมด! แต่ถ้าเราเปลี่ยนฟังก์ชั่นจาก non-async เป็น async ในตอนท้ายของเครือข่ายอ้างอิงนี้ นี่จะเป็นการเปลี่ยนแปลงที่เข้ากันไม่ได้ย้อนหลัง ... ยกเว้นว่าทุกฟังก์ชั่นจะเป็นแบบอะซิงก์และการเรียกฟังก์ชั่นทุกครั้งจะถูกรอโดยค่าเริ่มต้น

    และนั่นเป็นสิ่งที่ไม่พึงประสงค์อย่างมากเพราะมันมีผลกระทบด้านประสิทธิภาพที่ต่ำมาก คุณจะไม่สามารถคืนค่าราคาถูกได้ การเรียกใช้ฟังก์ชันทุกครั้งจะมีราคาแพงกว่ามาก

Async นั้นยอดเยี่ยม แต่ Async แบบนัยบางอย่างจะไม่ทำงานในความเป็นจริง

ภาษาที่ใช้งานได้จริงเช่น Haskell มีช่องทางหลบหนีเล็กน้อยเนื่องจากคำสั่งการดำเนินการส่วนใหญ่ไม่ระบุและไม่สามารถสังเกตเห็นได้ หรือใช้ถ้อยคำที่แตกต่าง: ลำดับการดำเนินการเฉพาะใด ๆ จะต้องเข้ารหัสอย่างชัดเจน ซึ่งค่อนข้างยุ่งยากสำหรับโปรแกรมในโลกแห่งความเป็นจริงโดยเฉพาะอย่างยิ่งโปรแกรม I / O-heavy ซึ่งรหัส async เหมาะอย่างยิ่ง


2
คุณไม่จำเป็นต้องใช้ระบบพิมพ์ Futures แบบโปร่งใสเช่น ECMAScript, Smalltalk, Self, Newspeak, Io, Ioke, Seph สามารถดำเนินการได้อย่างง่ายดายโดยไม่ต้องมีระบบ Tyoe หรือรองรับภาษา ใน Smalltalk และลูกหลานของมันวัตถุสามารถเปลี่ยนอัตลักษณ์ของมันได้อย่างโปร่งใสใน ECMAScript มันสามารถเปลี่ยนรูปร่างได้อย่างโปร่งใส นั่นคือทั้งหมดที่คุณต้องทำให้ฟิวเจอร์สโปร่งใสไม่ต้องการการสนับสนุนทางภาษาสำหรับอะซิงโครนัส
Jörg W Mittag

6
@ JörgWMittagฉันเข้าใจสิ่งที่คุณกำลังพูดและวิธีการที่สามารถทำงานได้ แต่ฟิวเจอร์โปร่งใสโดยไม่มีระบบประเภททำให้มันค่อนข้างยากที่จะมีฟิวเจอร์สชั้นหนึ่งพร้อมกันใช่ไหม? ฉันต้องการวิธีที่จะเลือกว่าฉันต้องการส่งข้อความไปยังอนาคตหรือมูลค่าในอนาคตโดยเฉพาะอย่างยิ่งสิ่งที่ดีกว่าsomeValue ifItIsAFuture [self| self messageIWantToSend]เพราะเป็นการยากที่จะรวมเข้ากับรหัสทั่วไป
amon

8
@amon "ฉันสามารถเขียนรหัส async ของฉันตามสัญญาและสัญญาเป็น monads" พระไม่จำเป็นจริงๆที่นี่ Thunks เป็นเพียงสัญญา เนื่องจากเกือบทุกค่าใน Haskell ได้รับการบรรจุกล่องค่าเกือบทั้งหมดใน Haskell จึงมีสัญญาอยู่แล้ว นั่นเป็นเหตุผลที่คุณสามารถโยนparรหัส Haskell ที่บริสุทธิ์ได้ทุกที่และรับ paralellism ได้ฟรี
DarthFennec

2
Async / คอยเตือนฉันถึงความต่อเนื่องของ Monad
les

3
ในความเป็นจริงทั้งสองข้อยกเว้นและ async / รอคอยเป็นกรณีของผลกระทบเกี่ยวกับพีชคณิต
Alex กลับ

21

สิ่งที่คุณขาดไปคือจุดประสงค์ของการดำเนินการแบบอะซิงโครนัส: ช่วยให้คุณใช้เวลารอคอยของคุณได้!

หากคุณเปิดการดำเนินการ async เช่นร้องขอทรัพยากรจากเซิร์ฟเวอร์ลงไปในการดำเนินการซิงโครโดยปริยายและทันทีที่รอการตอบกลับในกระทู้ของคุณไม่สามารถจะทำอะไรก็ได้กับเวลาที่รอคอย หากเซิร์ฟเวอร์ใช้เวลา 10 มิลลิวินาทีในการตอบสนองจะมี CPU ประมาณ 30 ล้านรอบที่เหลือ เวลาแฝงของการตอบสนองกลายเป็นเวลาดำเนินการสำหรับคำร้องขอ

เหตุผลเดียวที่โปรแกรมเมอร์คิดค้น async การดำเนินงานคือการซ่อนแฝงของงานโดยเนื้อแท้ยาวทำงานอยู่เบื้องหลังการคำนวณที่มีประโยชน์อื่นหากคุณสามารถเติมเวลารอคอยด้วยงานที่มีประโยชน์นั่นคือประหยัดเวลาของ CPU ถ้าคุณทำไม่ได้ก็ไม่มีอะไรหายไปจากการทำงานแบบอะซิงโครนัส

ดังนั้นฉันขอแนะนำให้โอบกอดการทำงานของ async ที่ภาษาของคุณมอบให้คุณ พวกเขาอยู่ที่นั่นเพื่อช่วยคุณประหยัดเวลา


ฉันคิดว่าภาษาที่ใช้งานได้ซึ่งการดำเนินการไม่ได้ปิดกั้นดังนั้นแม้ว่าจะมีไวยากรณ์ซิงโครนัสการคำนวณที่ใช้เวลานานจะไม่บล็อกเธรด
Cinn

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

2
คอมพิวเตอร์ทุกเครื่องจะรอด้วยความเร็วเท่ากัน
Bob Jarvis - Reinstate Monica

2
@BobJarvis ใช่ แต่พวกเขาแตกต่างกันในวิธีการทำงานมากที่พวกเขาจะได้กระทำในเวลาที่รอคอย ...
cmaster

13

บางคนทำ

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

ที่กล่าวว่ามันไม่ได้อย่างชัดเจนเป็นความคิดที่ดีที่จะทำทุก ความล้มเหลวที่พบบ่อยคือการโทรแบบซิงโครนัสในการวนซ้ำอย่างมีประสิทธิภาพ การมีการเรียกแบบอะซิงโครนัสนั้นเป็นการบอกเป็นนัยอาจปิดบังข้อผิดพลาดประเภทนั้น นอกจากนี้หากคุณสนับสนุนการบีบบังคับโดยปริยายจากTask<T>(หรือเทียบเท่าภาษาของคุณ) ไปTที่สามารถเพิ่มความซับซ้อน / ค่าใช้จ่ายเล็กน้อยให้กับเครื่องพิมพ์ดีดของคุณและการรายงานข้อผิดพลาดเมื่อไม่ชัดเจนซึ่งทั้งสองโปรแกรมต้องการจริงๆ

แต่นั่นไม่ใช่ปัญหาที่ผ่านไม่ได้ หากคุณต้องการสนับสนุนพฤติกรรมดังกล่าวคุณสามารถทำได้อย่างแน่นอนแม้ว่าจะมีการแลกเปลี่ยน


1
ผมคิดว่าความคิดอาจจะตัดทุกอย่างในฟังก์ชั่น async, งานซิงโครก็จะแก้ไขทันทีและเราได้รับทุกชนิดหนึ่งที่จะจับ (แก้ไข: @amon อธิบายว่าทำไมมันเป็นความคิดที่ไม่ดี ... )
Cinn

8
คุณช่วยยกตัวอย่างบางส่วนของ " บางคน " ได้ไหม
Bergi

2
การเขียนโปรแกรมแบบอะซิงโครนัสไม่ได้ใหม่ แต่อย่างใดในปัจจุบันผู้คนต้องรับมือกับมันบ่อยขึ้น
ลูกบาศก์

1
@Cubic - มันเป็นคุณสมบัติภาษาเท่าที่ฉันรู้ ก่อนที่มันจะเป็นเพียงฟังก์ชั่น userland (ที่น่าอึดอัดใจ)
Telastyn

12

มีภาษาที่ทำสิ่งนี้ แต่จริงๆแล้วมีความต้องการไม่มากเนื่องจากสามารถทำได้อย่างง่ายดายด้วยคุณสมบัติภาษาที่มีอยู่

ตราบใดที่คุณมีบางวิธีการแสดง asynchrony คุณสามารถใช้Futuresหรือสัญญาล้วนเป็นคุณสมบัติห้องสมุดคุณไม่จำเป็นต้องมีคุณสมบัติภาษาพิเศษใด ๆ และตราบใดที่คุณมีบางส่วนของการแสดงผู้รับมอบฉันทะใสคุณสามารถใส่คุณลักษณะทั้งสองเข้าด้วยกันและคุณมีฟิวเจอร์สใส

ตัวอย่างเช่นใน Smalltalk และลูกหลานของมันวัตถุสามารถเปลี่ยนเอกลักษณ์ของมันมันสามารถ "กลายเป็น" วัตถุที่แตกต่างกัน (และอันที่จริงวิธีการนี้เรียกว่าObject>>become:)

Future<Int>ลองนึกภาพการคำนวณยาวทำงานที่ส่งกลับ สิ่งนี้Future<Int>มีวิธีการแบบเดียวกันIntทั้งหมดยกเว้นการใช้งานที่แตกต่างกัน Future<Int>'s +วิธีการไม่เพิ่มจำนวนอีกและกลับผลที่ได้ก็จะส่งกลับใหม่Future<Int>ซึ่ง wraps คำนวณ และอื่น ๆ และอื่น ๆ. วิธีการที่ไม่สามารถนำมาใช้อย่างสมเหตุสมผลได้โดยการส่งคืนFuture<Int>จะแทนที่awaitผลลัพธ์โดยอัตโนมัติแล้วเรียกself become: result.ซึ่งจะทำให้วัตถุที่กำลังดำเนินการในปัจจุบัน ( selfเช่นตัวFuture<Int>) กลายเป็นresultวัตถุอย่างแท้จริงนั่นคือจากการอ้างอิงวัตถุที่เคยFuture<Int>เป็น ตอนนี้Intทุกที่โปร่งใสกับลูกค้าอย่างสมบูรณ์

ไม่จำเป็นต้องใช้คุณสมบัติพิเศษที่เกี่ยวข้องกับภาษาอะซิงโครนัส


ตกลง แต่นั่นมีปัญหาถ้าทั้งสองอย่างFuture<T>และTแบ่งปันบางส่วนที่ใช้ร่วมกันและฉันใช้ฟังก์ชั่นจากส่วนต่อประสาน มันควรจะbecomeเป็นผลแล้วใช้ฟังก์ชั่นหรือไม่? ฉันกำลังคิดถึงสิ่งต่าง ๆ เช่นตัวดำเนินการความเสมอภาคหรือการเป็นตัวแทนการตรวจแก้จุดบกพร่องสายอักขระ
amon

ฉันเข้าใจว่ามันไม่ได้เพิ่มคุณสมบัติใด ๆ สิ่งที่เรามีไวยากรณ์ที่แตกต่างกันในการเขียนการแก้ไขทันทีการคำนวณและการคำนวณระยะยาวและหลังจากนั้นเราจะใช้ผลลัพธ์แบบเดียวกันเพื่อวัตถุประสงค์อื่น ฉันสงสัยว่าถ้าเรามีไวยากรณ์ที่จัดการกับทั้งสองอย่างโปร่งใสทำให้อ่านได้ง่ายขึ้นและโปรแกรมเมอร์ไม่ต้องจัดการกับมัน เช่นเดียวกับการทำa + bทั้งจำนวนเต็มไม่สำคัญว่า a และ b จะพร้อมใช้งานทันทีหรือใหม่กว่าเราเพิ่งเขียนa + b(ทำให้เป็นไปได้Int + Future<Int>)
Cinn

@Cinn: ใช่คุณสามารถทำได้ด้วย Transparent Futures และคุณไม่จำเป็นต้องมีคุณสมบัติพิเศษทางภาษาในการทำเช่นนั้น คุณสามารถใช้มันได้โดยใช้ฟีเจอร์ที่มีอยู่แล้วเช่น Smalltalk, Self, Newspeak, Us, Korz, Io, Ioke, Seph, ECMAScript และเห็นได้ชัดว่าฉันเพิ่งอ่าน Python
Jörg W Mittag

3
@amon: แนวคิดของ Transparent Futuresคือคุณไม่รู้ว่ามันเป็นอนาคต จากมุมมองของคุณจะไม่มีการติดต่อกันระหว่างFuture<T>และTเพราะจากมุมมองของคุณ, ไม่มีFuture<T>Tเพียง ขณะนี้มีความท้าทายด้านวิศวกรรมมากมายเกี่ยวกับวิธีการทำให้มีประสิทธิภาพซึ่งการดำเนินงานควรจะมีการบล็อกเทียบกับการไม่บล็อก ฯลฯ แต่นั่นไม่ขึ้นกับว่าคุณจะใช้ภาษาหรือคุณสมบัติห้องสมุด ความโปร่งใสเป็นข้อกำหนดที่ระบุโดย OP ในคำถามฉันจะไม่เถียงว่ามันยากและอาจไม่สมเหตุสมผล
Jörg W Mittag

3
@ Jörgดูเหมือนว่าจะเป็นปัญหาในทุกสิ่งยกเว้นภาษาที่ใช้งานได้เนื่องจากคุณไม่มีทางรู้ว่าเมื่อใดที่โค้ดถูกเรียกใช้ในโมเดลนั้น ซึ่งโดยทั่วไปแล้วใช้งานได้ดีในการพูด Haskell แต่ฉันไม่สามารถดูว่ามันจะทำงานในภาษาขั้นตอนมากขึ้น (และแม้ใน Haskell ถ้าคุณสนใจเกี่ยวกับประสิทธิภาพการทำงานบางครั้งคุณต้องบังคับให้มีการดำเนินการ ความคิดที่น่าสนใจยังคงอยู่
Voo

7

พวกเขาทำ (ส่วนใหญ่แล้ว) คุณลักษณะที่คุณกำลังมองหาที่เรียกว่าหัวข้อ

เธรดมีปัญหาของตัวเองอย่างไรก็ตาม:

  1. เพราะรหัสสามารถระงับที่จุดใด ๆที่คุณไม่สามารถที่เคยถือว่าสิ่งที่จะไม่เปลี่ยนแปลง "ด้วยตัวเอง" เมื่อเขียนโปรแกรมด้วยเธรดคุณเสียเวลามากมายที่คิดว่าโปรแกรมของคุณควรจัดการกับสิ่งต่าง ๆ ที่เปลี่ยนแปลงไปอย่างไร

    ลองนึกภาพเซิร์ฟเวอร์เกมกำลังประมวลผลการโจมตีของผู้เล่นกับผู้เล่นอื่น บางสิ่งเช่นนี้

    if (playerInMeleeRange(attacker, victim)) {
        const damage = calculateAttackDamage(attacker, victim);
        if (victim.health <= damage) {
    
            // attacker gets whatever the victim was carrying as loot
            const loot = victim.getInventoryItems();
            attacker.addInventoryItems(loot);
            victim.removeInventoryItems(loot);
    
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon} and you die!");
            victim.setDead();
        } else {
            victim.health -= damage;
            victim.sendMessage("${attacker} hits you with a ${attacker.currentWeapon}!");
        }
        attacker.markAsKiller();
    }
    

    สามเดือนต่อมาผู้เล่นค้นพบว่าโดยการฆ่าและออกจากระบบอย่างแม่นยำเมื่อattacker.addInventoryItemsทำงานแล้วvictim.removeInventoryItemsจะล้มเหลวเขาสามารถเก็บรายการของเขาและผู้โจมตียังได้รับสำเนาของรายการของเขา เขาทำเช่นนี้หลายครั้งสร้างทองล้านตันออกมาจากอากาศและทำให้เศรษฐกิจของเกมแย่ลง

    ผู้โจมตีสามารถล็อกเอาต์ขณะที่เกมกำลังส่งข้อความถึงเหยื่อและเขาจะไม่ได้รับป้าย "ฆาตกร" เหนือหัวของเขาดังนั้นเหยื่อรายต่อไปของเขาจะไม่หนีจากเขา

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

    newItem.nextItem = list.firstItem;
    list.firstItem = newItem;
    

    นี่ไม่ใช่ปัญหาถ้าคุณบอกว่าเธรดสามารถถูกระงับได้เฉพาะเมื่อพวกเขากำลังทำ I / O เท่านั้น แต่ฉันแน่ใจว่าคุณสามารถจินตนาการถึงสถานการณ์ที่มีการดำเนินการ I / O เช่นการบันทึก:

    for (player = playerList.firstItem; player != null; player = item.nextPlayer) {
        debugLog("${item.name} is online, they get a gold star");
        // Oops! The player might've logged out while the log message was being written to disk, and now this will throw an exception and the remaining players won't get their gold stars.
        // Or the list might've been rearranged and some players might get two and some players might get none.
        player.addInventoryItem(InventoryItems.GoldStar);
    }
    
  3. เนื่องจากรหัสสามารถถูกหยุดชั่วคราว ณจุดใดก็ได้อาจมีสถานะมากมายที่จะบันทึก ระบบจัดการกับสิ่งนี้โดยให้แต่ละสแต็กแยกกันโดยสิ้นเชิง แต่สแต็กนั้นค่อนข้างใหญ่ดังนั้นคุณไม่สามารถมีเธรดได้มากกว่า 2000 รายการในโปรแกรม 32 บิต หรือคุณสามารถลดขนาดสแต็กได้โดยเสี่ยงต่อการทำให้มันเล็กเกินไป


3

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

ในขณะที่การโปรแกรมแบบอะซิงโครนัสโดยเนื้อแท้แล้วอะซิงโครนัสการ raison d'isontre ของการเขียนโปรแกรมแบบอะซิงโครนัสส่วนใหญ่จะหลีกเลี่ยงการบล็อกเธรดเคอร์เนล Node.js ใช้ asynchronosity ผ่าน callbacks หรือPromises เพื่ออนุญาตให้ทำการบล็อกการส่งจาก event loop และ Netty ใน Java ใช้ asynchronisity ผ่าน callbacks หรือCompletableFutures เพื่อทำสิ่งที่คล้ายกัน

รหัสไม่ปิดกั้นไม่ต้อง asynchronosity อย่างไร ขึ้นอยู่กับว่าภาษาการเขียนโปรแกรมและรันไทม์ของคุณเต็มใจให้คุณ

Go, Erlang และ Haskell / GHC สามารถจัดการสิ่งนี้ให้คุณได้ คุณสามารถเขียนสิ่งที่ชอบvar response = http.get('example.com/test')และปล่อยเคอร์เนลเธรดไว้เบื้องหลังขณะรอการตอบกลับ สิ่งนี้ทำโดย goroutines, กระบวนการ Erlang หรือforkIOปล่อยเคอร์เนลเธรดเบื้องหลังเมื่อทำการบล็อกอนุญาตให้ทำสิ่งอื่นในขณะรอการตอบกลับ

มันเป็นความจริงที่ภาษาไม่สามารถจัดการแบบอะซิงโครนัสให้คุณได้ แต่สิ่งที่เป็นนามธรรมบางอย่างทำให้คุณไปได้ไกลกว่าคนอื่น ๆ เช่นการต่อเนื่องที่ไม่ จำกัด หรือ Coroutines แบบอสมมาตร อย่างไรก็ตามสาเหตุหลักของรหัสแบบอะซิงโครนัสการบล็อกการเรียกระบบสามารถทำได้อย่างสมบูรณ์แยกออกจากผู้พัฒนาได้

Node.js และ Java รองรับโค้ดที่ไม่บล็อกแบบอะซิงโครนัสในขณะที่ Go และ Erlang สนับสนุนการไม่บล็อกแบบซิงโครนัสโค้ดที่ส ทั้งสองวิธีนี้ใช้ได้กับการแลกเปลี่ยนที่ต่างกัน

เหตุผลส่วนตัวของฉันคือการโต้เถียงกับ runtimes จัดการ non-blocking ในนามของผู้พัฒนาเป็นเหมือนการโต้เถียงกับการเก็บขยะในช่วงแรก ๆ ใช่มันมีค่าใช้จ่าย (ในกรณีนี้หน่วยความจำเพิ่มเติมเป็นหลัก) แต่มันทำให้การพัฒนาและการดีบักง่ายขึ้นและทำให้รหัสฐานแข็งแกร่งขึ้น

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


1
นี่เป็นคำตอบที่น่าสนใจจริงๆ! แต่ฉันไม่แน่ใจว่าฉันเข้าใจความแตกต่างระหว่างโค้ดที่ไม่บล็อก "ซิงโครนัส" และ "อะซิงโครนัส" สำหรับฉันโค้ดไม่บล็อกแบบซิงโครนัสหมายถึงบางสิ่งบางอย่างเช่นฟังก์ชั่น C แบบwaitpid(..., WNOHANG)ที่ล้มเหลวหากจำเป็นต้องบล็อก หรือ "แบบซิงโครนัส" ที่นี่หมายถึง "ไม่มีการเรียกกลับ / โปรแกรมเมอร์เครื่องรัฐ / เหตุการณ์ลูปที่มองเห็นได้โปรแกรมเมอร์? แต่สำหรับตัวอย่าง Go ของคุณฉันยังต้องรอผลอย่างชัดเจนจาก goroutine ด้วยการอ่านจากช่องใช่ไหม? async นี้มีค่าน้อยกว่า async / await ใน JS / C # / Python อย่างไร
amon

1
ฉันใช้ "อะซิงโครนัส" และ "ซิงโครนัส" เพื่อหารือเกี่ยวกับรูปแบบการเขียนโปรแกรมที่เปิดเผยต่อผู้พัฒนาและ "การปิดกั้น" และ "การไม่บล็อก" เพื่อหารือเกี่ยวกับการบล็อกเคอร์เนลเธรดระหว่างนั้น การคำนวณอื่น ๆ ที่ต้องทำและมีตัวประมวลผลเชิงตรรกะสำรองที่สามารถใช้ได้ โกโรไทน์สามารถรอผลได้โดยไม่ปิดกั้นเธรดที่สำคัญ แต่กอร์ไทน์ตัวอื่นสามารถสื่อสารกับมันผ่านช่องสัญญาณได้หากต้องการ โกโรไทน์ไม่จำเป็นต้องใช้แชนเนลโดยตรงเพื่อรอการอ่านซ็อกเก็ตที่ไม่มีการปิดกั้น
Louis Jackman

อืมฉันเข้าใจความแตกต่างของคุณแล้ว ในขณะที่ฉันกังวลเกี่ยวกับการจัดการข้อมูลและการควบคุมโฟลว์ระหว่าง coroutines คุณมีความกังวลมากขึ้นว่าจะไม่บล็อกเคอร์เนลเธรดหลัก ฉันไม่แน่ใจว่า Go หรือ Haskell มีข้อได้เปรียบเหนือ C ++ หรือ Java ในเรื่องนี้เนื่องจากพวกเขาสามารถเริ่มหัวข้อเธรดพื้นหลังได้เช่นกันเพียงแค่ต้องใช้โค้ดเพิ่มเติมอีกเล็กน้อย
amon

@LouisJackman สามารถอธิบายรายละเอียดเล็กน้อยเกี่ยวกับ async ที่ไม่บล็อกสำหรับการเขียนโปรแกรมระบบ ข้อดีของวิธีการแบบไม่บล็อคของ async มีอะไรบ้าง
Sunprophit

@sunprophit การไม่บล็อกแบบอะซิงโครนัสเป็นเพียงการแปลงคอมไพเลอร์ (โดยปกติคือ async / await) ในขณะที่การบล็อกแบบซิงโครนัสไม่ต้องการการสนับสนุนรันไทม์เช่นชุดผสมของการจัดการกองซ้อนที่ซับซ้อน การลด "(ต้องการ VM เช่น BEAM) ฯลฯ เช่นเดียวกับการรวบรวมขยะมันเป็นการแลกเปลี่ยนความซับซ้อนรันไทม์ที่น้อยลงเพื่อความสะดวกในการใช้งานและความทนทาน ภาษาระบบเช่น C, C ++ และ Rust หลีกเลี่ยงฟีเจอร์รันไทม์ที่ใหญ่กว่าเช่นนี้เนื่องจากโดเมนเป้าหมายของพวกเขาดังนั้นการไม่บล็อกแบบอะซิงโครนัสจึงเหมาะสมกว่า
Louis Jackman

2

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


2

ฉันขอขอบคุณคำถามและพบว่าคำตอบส่วนใหญ่เป็นเพียงการป้องกันสถานะเดิม ในสเปกตรัมของภาษาระดับต่ำถึงระดับสูงเราได้ติดอยู่ในร่องเป็นระยะเวลาหนึ่ง ระดับที่สูงขึ้นถัดไปจะเห็นได้ชัดว่าเป็นภาษาที่เน้นไวยากรณ์น้อยลง (ความต้องการคำหลักที่ชัดเจนเช่นรอและ async) และความตั้งใจมากขึ้น (เห็นได้ชัดว่าให้เครดิตแก่ Charles Simonyi แต่คิดถึงปี 2019 และอนาคต)

ถ้าฉันบอกโปรแกรมเมอร์ให้เขียนโค้ดบางอย่างที่ดึงค่าจากฐานข้อมูลได้อย่างปลอดภัยคุณสามารถสันนิษฐานได้ว่าฉันหมายถึง "และ BTW ไม่ต้องแขวน UI" และ "ไม่แนะนำข้อควรพิจารณาอื่นที่ยากที่จะหาข้อบกพร่อง " โปรแกรมเมอร์แห่งอนาคตที่มีภาษาและเครื่องมือรุ่นต่อไปจะสามารถเขียนโค้ดที่ดึงค่าในบรรทัดเดียวและจะไปจากที่นั่น

ภาษาระดับสูงสุดจะเป็นการพูดภาษาอังกฤษและอาศัยความสามารถของผู้ปฏิบัติงานในการรู้ว่าคุณต้องการทำอะไร (คิดว่าคอมพิวเตอร์ใน Star Trek หรือถามอะไรบางอย่างของ Alexa) เราห่างไกลจากจุดนั้น แต่ใกล้เข้ามามากขึ้นและความคาดหวังของฉันก็คือภาษา / คอมไพเลอร์อาจจะสร้างรหัสที่แข็งแกร่งและตั้งใจได้มากกว่าโดยไม่ต้องไปไกล ต้องการ AI

ในอีกด้านหนึ่งมีภาษาภาพใหม่กว่าเช่น Scratch ที่ทำสิ่งนี้และไม่จมอยู่กับเทคนิคการสร้างประโยคทั้งหมด แน่นอนว่ามีหลายอย่างอยู่เบื้องหลังการทำงานที่เกิดขึ้นเพื่อให้โปรแกรมเมอร์ไม่ต้องกังวลกับมัน ที่กล่าวว่าฉันไม่ได้เขียนซอฟต์แวร์ระดับธุรกิจใน Scratch ดังนั้นเช่นเดียวกับคุณฉันมีความคาดหวังเหมือนกันว่าถึงเวลาสำหรับภาษาการเขียนโปรแกรมสำหรับผู้ใหญ่เพื่อจัดการปัญหาซิงโครนัส / อะซิงโครนัสโดยอัตโนมัติ


1

ปัญหาที่คุณอธิบายคือสองเท่า

  • โปรแกรมที่คุณเขียนกำลังควรประพฤติถ่ายทอดสดเป็นทั้งเมื่อมองจากภายนอก
  • ไม่ควรมองเห็นได้ที่ไซต์การโทรว่าการเรียกใช้ฟังก์ชันอาจให้การควบคุมได้หรือไม่

มีสองวิธีในการบรรลุเป้าหมายนี้ แต่โดยทั่วไปแล้วต้มลงไป

  1. มีหลายกระทู้ (ในระดับที่เป็นนามธรรม)
  2. foo(4, 7, bar, quux)มีหลายชนิดของฟังก์ชั่นในระดับภาษาซึ่งทั้งหมดนี้จะเรียกว่าเช่นนี้

สำหรับ (1) ฉันกำลังรวบรวมและฟอร์กกระบวนการหลายกระบวนการวางไข่เคอร์เนลหลายเธรดและการประยุกต์ใช้เธรดสีเขียวที่จัดตารางเธรดระดับภาษารันไทม์ลงในเธรดเคอร์เนล จากมุมมองของปัญหาพวกเขาเหมือนกัน ในโลกนี้ฟังก์ชั่นไม่เคยให้ขึ้นหรือสูญเสียการควบคุมจากมุมมองของด้ายที่ ด้ายตัวเองบางครั้งก็ไม่ได้มีการควบคุมและบางครั้งก็ไม่ได้ทำงาน แต่คุณไม่ให้ขึ้นการควบคุมของด้ายของคุณเองในโลกนี้ ระบบที่เหมาะสมกับรุ่นนี้อาจมีหรือไม่มีความสามารถในการวางไข่เธรดใหม่หรือเข้าร่วมในเธรดที่มีอยู่ ระบบที่เหมาะสมรูปแบบนี้อาจจะหรืออาจไม่ได้มีความสามารถในการทำซ้ำด้ายเช่น forkUnix

(2) น่าสนใจ เพื่อความยุติธรรมเราต้องพูดคุยเกี่ยวกับรูปแบบการแนะนำและการกำจัด

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

คุณสามารถแนะนำฟังก์ชั่นแบบซิงโครนัสโดยใช้แลมบ์ดาและกำจัดมันด้วยการเรียกใช้ฟังก์ชัน

แนะนำฟังก์ชั่นซิงโคร:

((x) => {return x + x;})

การกำจัดฟังก์ชันซิงโครนัส:

f(4)

((x) => {return x + x;})(4)

คุณสามารถเปรียบเทียบความแตกต่างนี้ได้ด้วยการแนะนำและกำจัดฟังก์ชันอะซิงโครนัส

การแนะนำฟังก์ชั่นแบบอะซิงโครนัส

(async (x) => {return x + x;})

การกำจัดฟังก์ชัน Asynchonrous (หมายเหตุ: ใช้ได้ภายในasyncฟังก์ชันเท่านั้น)

await (async (x) => {return x + x;})(4)

ปัญหาพื้นฐานที่นี่ก็คือฟังก์ชั่นแบบอะซิงโครนัสก็เป็นฟังก์ชั่นแบบซิงโครนัสที่สร้างวัตถุสัญญาฟังก์ชั่นไม่ตรงกันนอกจากนี้ยังมีฟังก์ชั่นซิงโครการผลิตวัตถุสัญญา

นี่คือตัวอย่างของการเรียกใช้ฟังก์ชั่นอะซิงโครนัสในแบบจำลอง node.js

> (async (x) => {return x + x;})(4)
Promise { 8 }

คุณสามารถสันนิษฐานได้ว่าเป็นภาษาแม้แต่ภาษาที่พิมพ์แบบไดนามิกซึ่งความแตกต่างระหว่างการเรียกฟังก์ชันแบบอะซิงโครนัสและซิงโครนัสไม่สามารถมองเห็นได้ที่ไซต์การโทรและอาจไม่สามารถมองเห็นได้ที่ไซต์คำจำกัดความ

การใช้ภาษาอย่างนั้นและการลดลงสู่ Javascript นั้นเป็นไปได้คุณเพียงแค่ต้องทำให้ทุกฟังก์ชั่นไม่ตรงกัน


1

ด้วย goroutines ภาษา Go และเวลาใช้งานภาษา Go คุณสามารถเขียนโค้ดทั้งหมดได้ราวกับว่าเป็น synchrone หากการดำเนินการบล็อกในหนึ่ง goroutine การดำเนินการต่อใน goroutines อื่น และด้วยช่องคุณสามารถสื่อสารระหว่าง goroutines ได้อย่างง่ายดาย ซึ่งมักจะง่ายกว่าการเรียกกลับเช่นใน Javascript หรือ async / คอยในภาษาอื่น ๆ ดูhttps://tour.golang.org/concurrency/1สำหรับตัวอย่างและคำอธิบาย

นอกจากนี้ฉันไม่มีประสบการณ์ส่วนตัวกับมัน แต่ฉันได้ยินว่า Erlang มีสิ่งอำนวยความสะดวกที่คล้ายกัน

ดังนั้นใช่มีภาษาการเขียนโปรแกรม Like Go และ Erlang ซึ่งแก้ปัญหาการซิงค์ / อะซิงโครนัส แต่น่าเสียดายที่พวกเขายังไม่ได้รับความนิยมมาก เมื่อภาษาเหล่านั้นเติบโตในความนิยมบางทีสิ่งอำนวยความสะดวกที่พวกเขาจัดหาให้จะถูกนำไปใช้ในภาษาอื่นด้วย


ฉันแทบไม่เคยใช้ภาษาโก แต่ดูเหมือนว่าคุณจะประกาศอย่างชัดเจนgo ...ดังนั้นมันจึงดูเหมือนว่าawait ...ไม่ใช่หรือ
Cinn

1
@Cinn จริง ๆ แล้วไม่ใช่ คุณสามารถใส่โทรใด ๆ ที่เป็น goroutine บนของเส้นใยของตัวเอง / goเขียวด้ายด้วย และเกี่ยวกับการโทรใด ๆ ที่อาจปิดกั้นนั้นจะดำเนินการแบบอะซิงโครนัสโดยรันไทม์ซึ่งเพิ่งสลับไปยัง goroutine ที่แตกต่างกันในระหว่างนี้ (co-operative multi-tasking) คุณรอโดยรอข้อความ
Deduplicator

2
ในขณะที่ Goroutines เป็นประเภทของการเกิดขึ้นพร้อมกันฉันจะไม่ใส่ลงในที่ฝากข้อมูลเดียวกันกับ async / await: ไม่ใช่ coroutines แบบร่วมมือ แต่โดยอัตโนมัติ (และจองล่วงหน้า!) เธรดสีเขียวที่กำหนดไว้ แต่นี้ไม่ได้ให้รออัตโนมัติอย่างใดอย่างหนึ่งไปที่เทียบเท่ากับการอ่านจากช่องawait <- ch
อมร

@amon เท่าที่ฉันรู้ goroutines มีการกำหนดเวลาความร่วมมือในกระทู้พื้นเมือง (ปกติเพียงพอที่จะออกสูงสุดขนานฮาร์ดแวร์ที่แท้จริง) โดยรันไทม์และที่กำหนดไว้ล่วงหน้าโดยระบบปฏิบัติการ
Deduplicator

OP ขอให้ "สามารถเขียนโค้ดแบบอะซิงโครนัสในแบบซิงโครนัสได้" ดังที่คุณได้กล่าวถึงด้วย goroutines และ go runtime คุณสามารถทำสิ่งนั้นได้อย่างแน่นอน คุณไม่ต้องกังวลเกี่ยวกับรายละเอียดของเธรดเพียงแค่เขียนบล็อคอ่านและเขียนราวกับว่ารหัสนั้นเป็นแบบซิงโครนัสและ goroutines อื่น ๆ ของคุณจะยังคงทำงานต่อไป คุณไม่จำเป็นต้อง "รอ" หรืออ่านจากช่องเพื่อรับผลประโยชน์นี้ ฉันจึงคิดว่า Go เป็นภาษาโปรแกรมที่ตรงกับความต้องการของ OP มากที่สุด

1

มีแง่มุมที่สำคัญมากที่ยังไม่ได้รับการกล่าวถึง: การแสดงความเคารพ หากคุณมีรหัสอื่น ๆ (เช่น: วงเหตุการณ์) ที่ทำงานในระหว่างการโทรแบบ async (และถ้าคุณไม่มีแล้วทำไมคุณถึงต้องใช้ async?) รหัสนั้นอาจส่งผลกระทบต่อสถานะของโปรแกรม คุณไม่สามารถซ่อนการโทรแบบอะซิงก์จากผู้โทรเนื่องจากผู้โทรอาจขึ้นอยู่กับส่วนต่าง ๆ ของสถานะโปรแกรมที่จะไม่ได้รับผลกระทบในช่วงระยะเวลาของการเรียกฟังก์ชั่น ตัวอย่าง:

function foo( obj ) {
    obj.x = 2;
    bar();
    log( "obj.x equals 2: " + obj.x );
}

หากbar()เป็นฟังก์ชั่น async อาจเป็นไปได้ที่obj.xการเปลี่ยนแปลงระหว่างการดำเนินการ สิ่งนี้จะค่อนข้างคาดไม่ถึงโดยไม่มีคำใบ้ว่าแถบนั้นเป็นแบบอะซิงก์และเอฟเฟกต์นั้นเป็นไปได้ ทางเลือกเดียวคือการสงสัยว่าทุกฟังก์ชั่น / วิธีที่เป็นไปได้จะเป็นแบบ async และเรียกข้อมูลซ้ำและตรวจสอบสถานะใด ๆ นี่เป็นโอกาสที่จะเกิดข้อผิดพลาดที่ละเอียดอ่อนและอาจไม่สามารถทำได้เลยหากสถานะที่ไม่ใช่ท้องถิ่นบางอย่างถูกดึงผ่านฟังก์ชั่น ด้วยเหตุนี้โปรแกรมเมอร์จึงจำเป็นต้องทราบว่าฟังก์ชันใดมีศักยภาพในการเปลี่ยนแปลงสถานะของโปรแกรมด้วยวิธีที่ไม่คาดคิด:

async function foo( obj ) {
    obj.x = 2;
    await bar();
    log( "obj.x equals 2: " + obj.x );
}

ตอนนี้มันเห็นได้ชัดว่าbar()เป็นฟังก์ชั่น async และวิธีที่ถูกต้องในการจัดการมันคือการตรวจสอบค่าที่คาดหวังของobj.xภายหลังและจัดการกับการเปลี่ยนแปลงใด ๆ ที่อาจเกิดขึ้น

ดังที่ได้กล่าวไว้แล้วโดยคำตอบอื่น ๆ ภาษาที่ใช้งานได้อย่างแท้จริงเช่น Haskell สามารถหลบเลี่ยงเอฟเฟกต์นั้นได้โดยหลีกเลี่ยงความต้องการสถานะสาธารณะ / ส่วนกลางใด ๆ เลย ฉันไม่ได้มีประสบการณ์มากกับภาษาที่ใช้งานได้ดังนั้นฉันอาจจะลำเอียง แต่ฉันไม่คิดว่าการขาดสถานะโลกเป็นข้อได้เปรียบเมื่อเขียนแอปพลิเคชันขนาดใหญ่


0

ในกรณีของ Javascript ที่คุณใช้ในคำถามของคุณมีจุดสำคัญที่ต้องระวัง: Javascript เป็นแบบเธรดเดียวและรับประกันการดำเนินการตามลำดับตราบใดที่ไม่มีการโทรแบบ async

ดังนั้นถ้าคุณมีลำดับเหมือนคุณ:

const nbOfUsers = getNbOfUsers();

คุณรับประกันได้ว่าจะไม่มีอะไรดำเนินการในระหว่างนี้ ไม่จำเป็นต้องล็อคหรืออะไรที่คล้ายกัน

อย่างไรก็ตามถ้าgetNbOfUsersเป็นแบบอะซิงโครนัสแล้ว:

const nbOfUsers = await getNbOfUsers();

หมายความว่าในขณะที่getNbOfUsersรันการประมวลผลและโค้ดอื่น ๆ อาจทำงานในระหว่างนั้น สิ่งนี้อาจต้องมีการล็อคเกิดขึ้นขึ้นอยู่กับสิ่งที่คุณทำ

ดังนั้นจึงเป็นความคิดที่ดีที่ควรระวังเมื่อมีการโทรแบบอะซิงโครนัสและเมื่อไม่ได้เช่นในบางสถานการณ์คุณจะต้องใช้ความระมัดระวังเพิ่มเติมที่คุณไม่จำเป็นต้องใช้ถ้าการโทรนั้นเป็นแบบซิงโครนัส


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

@ กระป๋องนั่นไม่ใช่ประเด็นของฉัน จุดของฉันคือการไหลของการดำเนินการอาจไปยังส่วนอื่น ๆ ของรหัสของคุณในระหว่างการดำเนินการโทรแบบอะซิงโครนัสในขณะที่มันเป็นไปไม่ได้สำหรับการโทรแบบซิงโครนัส มันจะเหมือนกับมีหลายเธรดทำงานอยู่ แต่ไม่ได้รับรู้ สิ่งนี้อาจจบลงด้วยปัญหาใหญ่ ๆ (ซึ่งมักจะตรวจจับและทำซ้ำได้ยาก)
jcaron

-4

สิ่งนี้มีให้ใน C ++ std::asyncตั้งแต่ C ++ 11

เท็มเพลตฟังก์ชัน async รันฟังก์ชัน f แบบอะซิงโครนัส (อาจอยู่ในเธรดแยกซึ่งอาจเป็นส่วนหนึ่งของเธรดพูล) และส่งกลับ std :: future ที่จะเก็บผลลัพธ์ของการเรียกฟังก์ชันนั้นในที่สุด

และด้วย C ++ 20 coroutines สามารถใช้ได้:


5
ดูเหมือนจะไม่ตอบคำถาม ตามลิงค์ของคุณ: "Coroutines TS ให้อะไรเราสามคำภาษาใหม่: co_await, co_yield และ co_return" ... แต่คำถามคือทำไมเราต้องการคำหลักawait(หรือco_awaitในกรณีนี้) ในตอนแรก?
Arturo Torres Sánchez
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.