
تحتاج كل لغة تدعم الحوسبة المتوازية (التنافسية ، غير المتزامنة) إلى طريقة لتشغيل التعليمات البرمجية بالتوازي. فيما يلي أمثلة من واجهات برمجة التطبيقات المختلفة:
go myfunc(); // Golang pthread_create(&thread_id, NULL, &myfunc); /* C with POSIX threads */ spawn(modulename, myfuncname, []) % Erlang threading.Thread(target=myfunc).start() # Python with threads asyncio.create_task(myfunc()) # Python with asyncio
هناك العديد من الخيارات للتدوين والمصطلحات ، ولكن دلالات واحدة هي تشغيل myfunc
بالتوازي مع البرنامج الرئيسي ومتابعة مؤشر ترابط التنفيذ الأصل (المهندس. "التحكم في التدفق")
خيار آخر هو رد الاتصال :
QObject::connect(&emitter, SIGNAL(event()), // C++ with Qt &receiver, SLOT(myfunc())) g_signal_connect(emitter, "event", myfunc, NULL) /* C with GObject */ document.getElementById("myid").onclick = myfunc; // Javascript promise.then(myfunc, errorhandler) // Javascript with Promises deferred.addCallback(myfunc) # Python with Twisted future.add_done_callback(myfunc) # Python with asyncio
ومرة أخرى ، يتغير التدوين ، لكن كل الأمثلة تجعله يبدأ ، ابتداءً من اللحظة الحالية ، في حالة حدوث حدث معين ، myfunc
يبدأ حدث myfunc
. بمجرد ضبط رد الاتصال ، يتم إرجاع التحكم وتستمر وظيفة الاتصال. (في بعض الأحيان ، يتم التفاف عمليات الاسترجاعات في وظائف تجمع مريحة أو بروتوكولات ذات نمط ملتوي ، لكن الفكرة الأساسية لم تتغير.)
و ... هذا كل شيء. خذ أي لغة عامة للأغراض العامة المتزامنة وستجد على الأرجح أنها تقع في أحد هذه النماذج (في بعض الأحيان كلاهما غير متزامن).
ولكن ليس مكتبتي تريو غريبة جديدة. إنها لا تستخدم هذه الأساليب. بدلاً من ذلك ، إذا أردنا تشغيل myfunc
و anotherfunc
التوازي ، نكتب شيئًا مثل هذا:
async with trio.open_nursery() as nursery: nursery.start_soon(myfunc) nursery.start_soon(anotherfunc)
الحضانة - الحضانة ، الحضانة
لأول مرة تواجه تصميم "الحضانة" ، يضيع الناس. لماذا هناك مدير السياق (مع كتلة)؟ ما هي هذه الحضانة ، ولماذا تحتاج إلى تشغيل مهمة؟ ثم يفهم الناس أن الحضانة تتداخل مع الأساليب المعتادة في الأطر الأخرى وتغضب. يبدو كل شيء غريبًا ومحدداً وذو مستوى رفيع جدًا بحيث لا يكون بدائيًا أساسيًا. كل هذه ردود فعل مفهومة! لكن تحمل معها قليلا.
في هذه المقالة ، أريد أن أقنعك أن دور الحضانة ليست بدعة ، بل هي بدائية جديدة للتحكم في تدفق التنفيذ ، وهي أساسية مثل الحلقات ومكالمات الوظائف. علاوة على ذلك ، يجب التخلص من المناهج التي تمت مناقشتها أعلاه (إنشاء سلاسل وتسجيل عمليات الاسترجاعات) واستبدالها بمشاتل.
يبدو جريئة جدا؟ ولكن هذا قد حدث بالفعل: بمجرد استخدام goto
للتحكم في سلوك البرنامج. الآن هذه مناسبة للضحك:

لا تزال العديد من اللغات تحتوي على ما يسمى goto
، لكن قدراتها محدودة أكثر من goto
الأصلية. وفي معظم اللغات ، ليس الأمر على الإطلاق. ماذا حدث له؟ هذه القصة ذات صلة بشكل مدهش ، على الرغم من أنها غير مألوفة بالنسبة لمعظم العصور القديمة. دعنا نذكر أنفسنا بما goto
، ومن ثم نرى كيف يمكن أن يساعد ذلك في البرمجة غير المتزامنة.
جدول المحتويات
- ما هو غوتو؟
- ما هو الذهاب؟
- ماذا حدث للغوتو؟
- غوتو يدمر التجريد
- عالم جديد شجاع بدون غوتو
- لا مزيد من غوتو
- حول مخاطر تعبيرات النوع "Go"
- اذهب التعبيرات كسر التجريدات.
- تعبيرات الذهاب كسر التنظيف التلقائي للموارد المفتوحة.
- الذهاب التعبيرات كسر معالجة الأخطاء.
- لا مزيد من الذهاب
- الحضانة كبديل هيكلي للذهاب
- تحتفظ الحضانة بتجريد الوظائف.
- دعم الحضانة المهام الديناميكية المضافة.
- لا يزال بإمكانك مغادرة الحضانة.
- يمكنك تحديد الأنواع الجديدة التي الدجال كحضانة.
- لا ، ومع ذلك ، تنتظر دور الحضانة دائمًا إكمال جميع المهام في الداخل.
- يعمل التنظيف التلقائي للموارد.
- يعمل رفع الأخطاء.
- عالم جديد شجاع دون الذهاب
- مشاتل في الممارسة العملية
- النتائج
- تعليقات
- شكر
- الحواشي
- عن المؤلف
- تمديد
ما هو goto
؟
تمت برمجة أول أجهزة الكمبيوتر باستخدام المجمّع ، أو حتى أكثر بدائية. هذه ليست مريحة للغاية. لذلك في الخمسينيات ، بدأ أشخاص مثل John Backus من IBM و Grace Hopper من شركة Remington Rand في تطوير لغات مثل FORTRAN و FLOW-MATIC (المعروفة باسم سليلها المباشر COBOL ).
كان FLOW-MATIC طموحًا جدًا في ذلك الوقت. يمكنك التفكير في الأمر على أنه الجد الأكبر العظيم لبيثون - كانت أول لغة تم تطويرها بشكل أساسي للناس ، والثانية لأجهزة الكمبيوتر. بدا مثل هذا:

لاحظ أنه على عكس اللغات الحديثة ، لا توجد أي شرطية if
الكتل أو الحلقات أو المكالمات الوظيفية - في الواقع لا توجد كتل أو مسافات بادئة على الإطلاق. هذه مجرد قائمة تعبيرات متتابعة. ليس لأن هذا البرنامج قصير جدًا بحيث لا يحتاج إلى عبارات تحكم (بخلاف JUMP TO
) - لم يتم اختراع بناء الجملة هذا حتى الآن!

بدلاً من ذلك ، كان لدى FLOW-MATIC خياران للتحكم في تدفق التنفيذ. عادةً ما كان التدفق ثابتًا - ابدأ من الأعلى وانزل للأسفل ، تعبير واحد في المرة الواحدة. ولكن إذا قمت بتنفيذ تعبير JUMP TO
الخاص ، فقد تسيطر على مكان آخر. على سبيل المثال ، ينتقل التعبير (13) إلى التعبير (2):

كما هو الحال مع بدايات التوازي منذ بداية المقال ، لم يكن هناك اتفاق على ما يمكن تسميته هذه العملية "قفز باتجاه واحد". في القائمة ، هذا هو JUMP TO
، ولكن الانتقال goto
الجذر تاريخياً (مثل "اذهب إلى هناك") ، والذي أستخدمه هنا.
فيما يلي مجموعة كاملة من الانتقال goto
هذا البرنامج الصغير:

هذا يبدو مربكا ليس فقط لك! ورثت FLOW-MATIC هذا النمط القائم على القفز مباشرة من المجمّع. إنها قوية ، قريبة جدًا من كيفية عمل أجهزة الكمبيوتر فعليًا ، لكن من الصعب للغاية العمل معها مباشرة. كرة الأسهم هذه هي سبب اختراع المصطلح "رمز السباغيتي".
ولكن لماذا goto
تسبب مثل هذه المشكلة؟ لماذا بعض بيانات الرقابة جيدة والبعض الآخر لا؟ كيفية اختيار الجيد منها؟ في ذلك الوقت كان الأمر غير مفهوم تمامًا ، وإذا لم تفهم المشكلة ، فمن الصعب حلها.
ما هو go
؟
دعنا نستنتج من قصتنا. يعلم الجميع أن goto
كان سيئًا ، لكن ما علاقة ذلك بعدم التزامن؟ انظر إلى تعبير go
الشهير من Golang ، والذي يستخدم لتفرخ "goroutine" الجديد (تيار خفيف الوزن):
// Golang go myfunc();
هل من الممكن رسم مخطط لتدفق التنفيذ؟ إنه يختلف قليلاً عن المخطط أعلاه ، لأنه هنا يتم تقسيم الدفق. دعونا نرسمها مثل هذا:

تهدف الألوان هنا إلى إظهار اختيار كلا المسارين. من وجهة نظر goroutine الأصل (الخط الأخضر) - يتم تنفيذ تدفق التحكم بالتتابع: يبدأ من أعلى ثم ينخفض فورًا. في هذه الأثناء ، من وجهة نظر الدالة التنازلية (خط أرجواني) ، يأتي التيار من الأعلى ثم ينتقل إلى جسم myfunc
. على عكس مكالمة الوظائف العادية ، هناك قفزة في اتجاه واحد - بدءًا من myfunc
إلى مكدس جديد تمامًا وينسى وقت التشغيل فورًا من أين أتينا.
على ما يبدو أقصد المكدس الدعوة
لكن هذا لا ينطبق فقط على Golang. هذا الرسم البياني صحيح بالنسبة لجميع البدائل (الضوابط) المدرجة في بداية المقال:
- عادةً ما تقوم مكتبات الترابط بإرجاع نوع من عنصر التحكم الذي سيسمح لها بالانضمام إلى سلسلة الرسائل لاحقًا - ولكن هذه عملية مستقلة لا تعرف اللغة نفسها شيئًا عنها. يحتوي الرسم الأساسي لإنشاء سلسلة رسائل جديدة على الرسم البياني الموضح أعلاه.
- يُعد تسجيل رد الاتصال مساويا للدلالة على إنشاء سلسلة رسائل خلفية (على الرغم من أنه من الواضح أن التنفيذ مختلف) ، وهو:
أ) يتم حظره حتى يحدث الحدث ، ثم
ب) تطلق وظيفة رد الاتصال
لذلك ، فيما يتعلق بمشغلي التحكم رفيعي المستوى ، يعد تسجيل رد الاتصال تعبيرًا متطابقًا. - مع
Futures
and Promises
نفس الشيء - عند تشغيل الوظيفة وإرجاع Promise
، فهذا يعني أنها تخطط للعمل في الخلفية وإرجاع كائن تحكم للحصول على النتيجة في وقت لاحق (إذا كنت تريد). من وجهة نظر دلالات الإدارة ، هو نفسه إنشاء تدفق. بعد ذلك ، يمكنك تمرير رد الاتصال إلى Promis ثم كما هو الحال في الفقرة السابقة.
يظهر هذا النمط نفسه في أشكال عديدة - التشابه الرئيسي هو أنه في جميع هذه الحالات يتم تقسيم تدفق التحكم - يتم الانتقال إلى الخيط الجديد ، لكن الأصل يرجع إلى الشخص الذي أطلق عليه. معرفة ما الذي تبحث عنه ، سترى ذلك في كل مكان! هذه لعبة مثيرة للاهتمام (على الأقل لبعض أنواع الأشخاص)!
ومع ذلك ، يزعجني أنه لا يوجد اسم قياسي لهذه الفئة من عبارات التحكم. أستخدم تعبير "go" للاتصال بهم ، تمامًا كما أصبح مصطلح "goto" مصطلحًا عامًا لجميع التعبيرات التي goto
. لماذا go
؟ أحد الأسباب هو أن Golang يعطينا مثالًا نظيفًا جدًا على بناء الجملة هذا. والآخر هو:

لاحظ التشابه؟ هذا صحيح - go
هو واحد من أشكال goto
.
تشتهر البرامج غير المتزامنة بصعوبة الكتابة والتحليل. وكذلك البرامج القائمة على goto
. يتم حل المشكلات التي تسببها goto
باللغات الحديثة. إذا تعلمنا كيفية إصلاح goto
، فهل سيساعد ذلك على إنشاء واجهات برمجة تطبيقات غير متزامنة أكثر ملاءمة؟ لنكتشف!
ماذا حدث goto
؟
إذاً ما الخطأ في الانتقال goto
العديد من المشكلات؟ في أواخر الستينيات من القرن الماضي ، كتب Edsger Wieb Dijkstra زوجين من الأعمال المعروفة التي ساعدت على فهم هذا الأمر بشكل أكثر وضوحًا: الحجج ضد مشغل goto و Notes حول البرمجة الهيكلية .
goto
يدمر التجريد
في هذه الأعمال ، قلق Dijkstra حول كيفية كتابة برامج غير تافهة وضمان صحتها. هناك العديد من النقاط المثيرة للاهتمام. على سبيل المثال ، ربما سمعت هذه العبارة:
يمكن أن تُظهر برامج الاختبار وجود أخطاء ، ولكن لا تُظهر غيابها أبدًا.
نعم ، هذا من ملاحظات البرمجة الهيكلية . لكن قلقه الرئيسي كان التجريد . لقد أراد أن يكتب برامج أكبر من أن يضعها في رؤوسهم. للقيام بذلك ، يجب عليك التعامل مع أجزاء البرنامج كمربعات سوداء - على سبيل المثال ، ترى هذا البرنامج في Python:
print("Hello World!")
ولن تحتاج إلى معرفة كل تفاصيل كيفية print
(تنسيق الخطوط ، التخزين المؤقت ، اختلافات النظام الأساسي ، وما إلى ذلك). كل ما تحتاج إلى معرفته هو أن print
تطبع بطريقة ما النص الذي مررت به ، ويمكنك التركيز على ما تريد القيام به في هذه الشفرة. أراد ديكسترا اللغات لدعم هذا النوع من التجريد.
في هذه المرحلة ، تم إنشاء بناء جملة الكتلة وتراكمت لغات مثل ALGOL ~ 5 أنواع مختلفة من عبارات التحكم: لا يزال لديهم مؤشر ترابط متتابع للتنفيذ و goto
:

واكتسبت أيضًا الظروف والدورات ومكالمات الوظائف:

يمكنك تنفيذ هذه التركيبات عالية المستوى باستخدام goto
، وهذا ما فكر به الناس من قبل: كاختصار مناسب. لكن Dijkstra أشار إلى الفرق الكبير بين goto
وبقية مشغلي التحكم. لكل شيء إلا goto
، موضوع التنفيذ
- يأتي من أعلى => [يحدث شيء] => يأتي التدفق من أسفل
يمكننا أن نسمي هذا "قاعدة الصندوق الأسود" - إذا كانت بنية التحكم (مشغل التحكم) تحتوي على هذا النموذج ، ثم في موقف لا تهتم فيه بالتفاصيل في الداخل ، يمكنك تجاهل الجزء "يحدث شيء ما" وتعامل الكتلة كما في متسلسل منتظم فريق. والأفضل من ذلك ، ينطبق ذلك على أي كود يتكون من هذه الكتل. عندما أنظر إلى:
print("Hello World!")
لست بحاجة إلى قراءة مصادر print
وجميع التبعيات الخاصة بها لفهم أين سيذهب مؤشر ترابط التنفيذ. ربما توجد داخل الحلقة حلقة ، وفيها هناك شرط لاستدعاء وظيفة أخرى ... كل ذلك ليس مهمًا - أعرف أن الخيط سينتقل print
، وستقوم الوظيفة بمهمتها ، وفي نهاية المطاف سيعود الخيط إلى الكود الذي أستخدمه قرأت.
ولكن إذا كان لديك لغة باستخدام goto
- لغة يتم فيها إنشاء الوظائف وكل شيء آخر على أساس goto
، ويمكن الانتقال إلى أي مكان وفي أي وقت - فإن هذه الهياكل ليست مربعات سوداء على الإطلاق! إذا كانت لديك وظيفة بها حلقة ، يوجد بداخلها شرط ، وداخلها يوجد goto
... عندها يمكن أن تمر هذه goto
بالإعدام في أي مكان. ربما تعود السيطرة فجأة بالكامل من وظيفة أخرى لم تقم حتى بالاتصال بها! أنت لا تعرف!
وهذا يكسر التجريد - أي وظيفة يمكن أن يكون لها goto
محتمل في الداخل ، والطريقة الوحيدة لمعرفة ما إذا كانت هذه هي الحالة هي وضع كل شفرة المصدر لنظامك في الاعتبار. بمجرد أن تحتوي اللغة على goto
، لا يمكنك التنبؤ بتدفق التنفيذ. لهذا السبب goto
يؤدي إلى رمز السباغيتي.
وبمجرد أن فهم ديكسترا المشكلة ، تمكن من حلها. هذا هو افتراضه الثوري - يجب ألا نفكر في الظروف / الحلقات / الدعوات الوظيفية باعتبارها اختصارات لـ goto
، ولكن كأولويات أساسية مع حقوقنا - وعلينا إزالة goto
بالكامل من goto
.
من عام 2018 ، يبدو هذا واضحًا جدًا. لكن كيف يتفاعل المبرمجون عند محاولة التقاط ألعابهم غير الآمنة؟ في عام 1969 ، بدا اقتراح ديكسترا مشكوك فيه بشكل لا يصدق. ودافع دونالد نوث goto
. كان الأشخاص الذين أصبحوا خبراء في الترميز باستخدام goto
مستاءين بحق من الاضطرار إلى إعادة تعلم كيفية التعبير عن أفكارهم بعبارات جديدة أكثر تقييدًا. وبالطبع ، فقد تطلب الأمر إنشاء لغات جديدة تمامًا.
ونتيجة لذلك ، فإن اللغات الحديثة أقل صرامةً قليلاً من الصياغة الأصلية لديجسترا.

اليسار: goto
التقليدية. اليمين: goto
المستأنس ، كما هو الحال في C ، C # ، Golang ، إلخ. الفشل في عبور حدود الوظيفة يعني أنه لا يزال بإمكانه التبول على حذائك ، ولكن من غير المرجح أن يزعجك.
إنها تتيح لك القفز على مستويات تداخل عبارات التحكم الهيكلي باستخدام بيانات break
أو return
أو return
. ولكن على المستوى الأساسي ، يتم تصميمهم جميعًا حول فكرة ديكسترا ويمكن أن يعطلوا التدفق المتسلسل للتنفيذ بطريقة محدودة للغاية. على وجه الخصوص ، وظائف - أداة أساسية لف خيط التنفيذ في الصندوق الأسود - غير قابلة للتدمير. لا يمكنك تنفيذ أمر break
من وظيفة إلى أخرى ولا يمكن للرجوع العودة إليك أكثر من الوظيفة الحالية. لن يؤثر أي معالجة لمؤشر التنفيذ داخل الوظيفة على وظائف أخرى.
واللغات التي حافظت على مشغل goto
(C ، C # ، Golang ، ...) حدته بشدة. كحد أدنى ، لا تسمح لك بالانتقال من جسم وظيفة إلى أخرى. إذا كنت لا تستخدم Assembler [2] ، فإن اللعبة التقليدية الكلاسيكية غير المحدودة هي شيء من الماضي. فاز ديكسترا.
عالم جديد شجاع بدون غوتو
حدث شيء مثير للاهتمام مع اختفاء goto
- فقد تمكن منشئو اللغة من بدء إضافة ميزات جديدة تستند إلى تدفق منظم للتنفيذ.
على سبيل المثال ، يحتوي Python على بناء جملة رائع لمسح الموارد تلقائيًا - مدير السياق . يمكنك الكتابة:
وهذا يضمن أن يتم فتح الملف في وقت التشغيل some code
ولكن بعد ذلك - إغلاق على الفور. معظم اللغات الحديثة لها معادلات ( RAII ، using
، جرّب الموارد ، defer
، ...). ويفترضون جميعًا أن تدفق التحكم في محله. وماذا يحدث إذا قفزنا إلى الكتلة باستخدام goto
؟ هل الملف مفتوح أم لا؟ وإذا قفزنا من هناك بدلاً من المغادرة كالمعتاد؟
بعد اكتمال الكود الموجود داخل الكتلة ، يبدأ تشغيل __exit__()
الذي يغلق الموارد المفتوحة ، مثل الملفات والاتصالات.
هل سيتم إغلاق الملف؟ في goto
، ببساطة لا يعمل مديرو السياق بطريقة واضحة.
نفس المشكلة في معالجة الأخطاء - عندما يحدث خطأ ما ، ما الذي يجب أن تفعله الكود؟ في كثير من الأحيان - إرسال وصف للخطأ في مكدس (من المكالمات) إلى رمز الاتصال والسماح لها بتقرير ما يجب القيام به. تحتوي اللغات الحديثة على إنشاءات خاصة لهذا ، مثل الاستثناءات أو أشكال أخرى من رفع الخطأ التلقائي . لكن هذه المساعدة لا تتوفر إلا إذا كانت اللغة تحتوي على مكدس للمكالمات ومفهوم "مكالمة" قوي. استرجع السباغيتي في مثال التدفق في مثال FLOW-MATIC وتخيل الاستثناء الذي ألقيت في الوسط. أين يمكن أن يأتي حتى؟
لا مزيد من goto
لذا ، فإن الصورة التقليدية - التي تتجاهل حدود الوظائف - سيئة ، ليس فقط لأنه من الصعب استخدامها بشكل صحيح. إذا كان هذا فقط ، فيمكن أن يبقى goto
- بقي الكثير من بنى اللغة السيئة.
لكن حتى ميزة goto
ذاتها في اللغة تجعل كل شيء أكثر تعقيدًا. لا يمكن اعتبار مكتبات الجهات الخارجية صندوقًا أسودًا - دون معرفة المصدر ، لا يمكنك معرفة الوظائف العادية والتي تتحكم بشكل غير متوقع في تدفق التنفيذ. هذه عقبة رئيسية أمام التنبؤ بسلوك الكود المحلي. كما يتم فقد ميزات قوية مثل مديري السياق والنوافذ المنبثقة للخطأ التلقائي. من الأفضل إزالة goto
تمامًا ، لصالح شركات التحكم التي تدعم قاعدة الصندوق الأسود.
حول مخاطر التعبيرات مثل "Go"
لذلك ، نظرنا goto
قصة goto
. ولكن هل ينطبق ذلك على مشغل go
؟ حسنا ... الكل في الكل! التشبيه دقيق بشكل مثير للصدمة.
اذهب التعبيرات كسر التجريدات.
تذكر كيف قلنا أنه إذا سمحت اللغة بـ goto
، goto
أي وظيفة إخفاء goto
بحد ذاتها؟ في معظم الأطر غير المتزامنة ، تؤدي تعبيرات go
إلى نفس المشكلة - قد تقوم (أو لا) تقوم بأي مهمة بتشغيل المهمة في الخلفية. يبدو أن الوظيفة قد أعادت السيطرة ، لكن هل ما زالت تعمل في الخلفية؟ وليس هناك طريقة لمعرفة ذلك دون قراءة مصدر الوظيفة وكل ما تستدعيه. ومتى ينتهي؟ من الصعب القول. إذا go
، ونظائرها ، فإن الوظائف لم تعد صناديق سوداء تحترم تدفق التنفيذ. في مقالتي الأولى حول واجهات برمجة التطبيقات غير المتزامنة ، وصفت هذا بأنه "انتهاك السببية" ووجدت أن هذا هو السبب الجذري للعديد من المشكلات الشائعة والحقيقية في البرامج التي تستخدم asyncio
والملتوية ، مثل مشاكل التحكم في التدفق ، ومشاكل الإغلاق المناسب ، إلخ.
يشير هذا إلى التحكم في تدفق البيانات التي تدخل وتخرج من البرنامج. على سبيل المثال ، يستقبل البرنامج البيانات بسرعة 3 ميجابايت / ثانية ، ويترك بسرعة تبلغ 1 ميجابايت / ثانية ، وبالتالي يستهلك البرنامج المزيد والمزيد من الذاكرة ، راجع مقالة أخرى للمؤلف
تعبيرات الذهاب كسر التنظيف التلقائي للموارد المفتوحة.
دعونا نلقي نظرة على مثال with
بيان مرة أخرى:
قلنا في وقت سابق أننا "مضمونون" أن الملف سيكون مفتوحًا أثناء عمل some code
، ثم إغلاقه بعد ذلك. لكن ماذا لو أن some code
يبدأ مهمة خلفية؟ : , , with
, with
, , , . , ; , , some code
.
, , - , , .
, Python threading
— , , — , with
, , , ( ). , . , .
go- .
, , (exceptions), . " ". , . , , . , , … , . , - . ( , , " - " — ; .) Rust — , , - — . (thread) , Rust .
, , join , errbacks Twisted Promise.catch Javascript . , , . , Traceback . Promise.catch
.
, .
go
, goto
, go- — , , , . , goto
, , go
.
, , ! :
, Trio .
go
: , , , . , , :

, , , " " .
? " " ,
) , , ( ),
) , .
. , - . , .. [3]
: , , , "" , . Trio , async with
:

, as nursery
nursery
. nursery.start_soon()
, () : myfunc
anotherfunc
. . , , () , , .

, , — , , . , .
, .
:

, . هؤلاء بعض منهم:
.
go- — , , , . — , , . , , .
.
, . :
run_concurrently([myfunc, anotherfunc])
async.gather Python, Promise.all Javascript, ..
, , , . , accept
, .
accept
Trio:
async with trio.open_nursery() as nursery: while True: incoming_connection = await server_socket.accept() nursery.start_soon(connection_handler, incoming_connection)
, , run_concurrently
. , run_concurrently
— , , run_concurrently
, .
.
. , , ? : . , async with open_nursery()
nursery.start_soon()
, — [4], , , . , , .
, , " ", :
, .
, , go-, .
, .
, - . , , . :
async with my_supervisor_library.open_supervisor() as nursery_alike: nursery_alike.start_soon(...)
, , . .
Trio , asyncio
: start_soon()
, Future
( , Future
). , ( , Trio Future
!), .
, , .
, , — — .
Trio, . , , " " ( ), Cancelled
. , , — - , " ", , .. , , . , , , .
.
" ", with
. , with
, .
.
, , . .
Trio, , … - . , . , — " " — , myfunc
anotherfunc
, . , , .
, : (re-raise) , . ,
" " , , , , , .
, , . ?
— ( ) , . , , , , .
, , - ( task cancellation ). C# Golang, — .
go
goto
, with
; go
- . على سبيل المثال:
- , , , . ( : - )
- — Python ,
ctrl-C
( ). , .
, . ?
… : ! , , . , , , break
continue
.
, . — , 1970 , goto
.
. (Knuth, 1974, .275):
, goto
, , " " goto
. goto
! , , goto
, . , , . , — , — "goto" .
: . , , . , , . , , .
, Happy Eyeballs ( RFC 8305 ), TCP . , — , , . Twisted — 600 Python . 15 . , , , . , , . , . ? . .
النتائج
— go
, , , Futures
, Promises
,… — goto
, . goto
, -- goto
, . , , ; , . , goto
, .
, , ( CTRL+C
) , .
, , , , — , goto
. FLOW-MATIC , , - . , , Trio , , .
Trio .
:
Trio : https://trio.discourse.group/
شكر
Graydon Hoare, Quentin Pradet, and Hynek Schlawack . , , .
berez .
: FLOW-MATIC (PDF), .
Wolves in Action, Martin Pannier, CC-BY-SA 2.0 , .
, Daniel Borker, CC0 public domain dedication .
[2] WebAssembly , goto
: ,
[3] , , , , :
The "parallel composition" operator in Cooperating/Communicating Sequential Processes and Occam, the fork/join model, Erlang supervisors, Martin Sústrik's libdill , crossbeam::scope / rayon::scope Rust. golang.org/x/sync/errgroup github.com/oklog/run Golang.
, - .
[4] start_soon()
, , start_soon
, , , . , .
عن المؤلف
Nathaniel J. Smith , Ph.D., UC Berkeley numpy
, Python . Nathaniel .
:
, , , Haskell , , .
( , 0xd34df00d , ) , ( Happy Eyeballs ), .
, Trio ? Haskell Golang ?
: