عالم بدون كوروتينات. عكازات للمبرمج - أسينسيو

1 المقدمة



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



نحن مبرمجون. ربما بدرجات متفاوتة ، ولكن البعض - بالتأكيد. هذا أعني أننا مختلفون ويمكننا أيضًا التفكير بطرق مختلفة. إن العبارة القائلة بأن المبرمج لا يفكر إلا بشكل ثابت هي فقط من جانب واحد ، وضارة وحتى تجديفية ، مثل حقيقة أن الشخص يركض فقط. في بعض الأحيان - ويطير. البعض ، مثل الطيارين ، يفعلون ذلك بانتظام ، والبعض الآخر ، مثل رواد الفضاء ، حتى لأشهر وباستمرار. فكرة التفكير المتسق تقلل من قدرة الإنسان. في مرحلة ما ولفترة من الوقت ، يمكنك حتى تصديق ذلك ، ولكن "لا يزال يتحول" - هذا يتعلق بحقيقة أن الحياة ستلحق خسائرها عاجلاً أم آجلاً.



Asyncio في Python هو عكاز برمجي يحاكي ، من الناحية المجازية ، رحلة التفكير الموازي غير الصحيح. نوع من القفز بالأيدي. يبدو ، في بعض الأحيان ، مضحك وخرقاء. على الرغم من أن هذا يعد أيضًا مخرجًا في موقف معين: يمكنك فقط عبور البركة والتلوث ، ولكن إذا سمحت القوة ، فمن الأفضل القفز فوقها. لكن ربما يفتقر المبرمجون إلى القوة؟



دعونا نحاول التخلص من "عكازات البرامج" المفروضة والارتفاع فوق روتين البرنامج. ودعها لا تكون قفزة ، أو ربما ليست عالية وطويلة ، لكنها لا تزال ، خاصة بالمقارنة مع العكازات ، رحلة. بعد كل شيء ، مرة واحدة الكسندر فيودوروفيتش Mozhaisky (يجب عدم الخلط بينه وبين محكمة مدينة Mozhaisky في منطقة موسكو ؛) أو نفس الأخوة رايت تغلبوا على عدة مئات من الأمتار في الهواء لأول مرة. نعم ، وتبدأ اختبارات الطائرات الحديثة بالجري والفصل قصير المدى عن المدرج.



2. مثال بسيط للغاية مع أسينسيو



سنبدأ بالطيران في بايثون. برنامج الرحلة بسيط. هناك طائرات (مع ذلك ، في النسخة الأصلية من صور العناكب ، انظر [1] ) بأسماء على جسم الطائرة "مدونة" ، "أخبار" ، "منتدى". تقلع في نفس الوقت. يجب على الجميع التحليق بجزء من المسار في وقت معين ورمي ، على سبيل المثال ، علمًا برقم المقطع المغطى. يجب أن يتم ذلك ثلاث مرات. وعندها فقط تهبط.



في Python ، يتم وصف نموذج لهذا السلوك ثم محاكاته بواسطة الكود الموجود في القائمة 1.



قائمة 1. كود بايثون لطائرات العنكبوت
import asyncio
import time

async def spider(site_name):
 for page in range(1, 4):
     await asyncio.sleep(1)
     print(site_name, page)

spiders = [
 asyncio.ensure_future(spider("Blog")),
 asyncio.ensure_future(spider("News")),
 asyncio.ensure_future(spider("Forum"))
]

start = time.time()

event_loop = asyncio.get_event_loop()
event_loop.run_until_complete(asyncio.gather(*spiders))
event_loop.close()

print("{:.2F}".format(time.time() - start))




نتائج المحاكاة لمثل هذه "الرحلة" هي كما يلي:



مدونة 1

أخبار 1

منتدى 1

مدونة 2

أخبار 2

منتدى 2

مدونة 3

أخبار 3

منتدى 3

3.00



لماذا يتم شرح هذا بالتفصيل بواسطة الفيديو [1]. لكننا نحن أصحاب الخيال والرحلة المتزامنة (وفقًا للسيناريو - غير المتزامن) لـ " طائراتنا " الثلاث دون استخدام asyncio ستقدم بطريقة مختلفة - على أساس النماذج التلقائية. على سبيل المثال ، تُظهر القائمة 2 رمز تأخير التشغيل التلقائي ، وهو تناظرية للتأخير غير المتزامن من الوحدة النمطية غير المتزامن ، ويمثله السطر في انتظار asyncio.sleep (1) في القائمة 1.



سرد 2. كود التأخير التلقائي في بايثون
import time

class PSleep:
    def __init__(self, t, p_FSM): self.SetTime = t; self.nState = 0; self.bIfLoop = False; self.p_mainFSM = p_FSM
    def x1(self): return time.time() - self.t0 <= self.SetTime
    def y1(self): self.t0 = time.time()
    def loop(self):
        if (self.nState == 0): self.y1(); self.nState = 1
        elif (self.nState == 1):
            if (not self.x1()): self.nState = 4




يتم تمرير قيمة التأخير والمؤشر إلى الكائن الذي أنشأ كائن التأخير عبر مُنشئ الفئة. المؤشر مطلوب من قبل وظيفة التحكم في العملية ، والتي ، بعد إزالة التأخير ، ستستمر في العملية الأصلية التي تم إيقافها عند إنشائها.



تُظهر القائمة 3 نظير التشغيل الآلي لمستوى العنكبوت غير المتزامن (انظر أيضًا القائمة 1). من المحتمل جدًا ألا يحلم خبير برمجة بايثون بهذا ، حتى في أكثر الكوابيس! نمت شفرة المصدر المكونة من أربعة أسطر 15 مرة! أليس هذا سببًا للإعجاب بكود Python النموذجي بشكل عام و asycio بشكل خاص ، أو ، على الأقل ، دليل على ميزة "coroutine Technology" على البرمجة التلقائية؟



قائمة 3. رمز لعنكبوت آلي في بايثون
# ""   "Blog"
class PBSpider:
    def __init__(self, name):
        self.nState = 0; self.bIfLoop = True; self.site_name = name; self.page = 1;
        self.p_mainFSM = b_sleep;
    def x1(self): return self.page < 4
    def y1(self):
        self.bIfLoop = False; automaton.append(b_sleep);
        b_sleep.p_mainFSM = blog
        automaton[-1].bIfLoop = True;
        automaton[-1].nState = 0
    def y2(self): print(self.site_name, self.page)
    def y3(self): self.page += 1
    def y4(self): self.page = 1
    def loop(self):
        if (self.x1() and self.nState == 0):  self.y1(); self.nState = 1
        elif (not self.x1()  and self.nState == 0): self.y1(); self.y4(); self.nState = 33
        elif (self.nState == 1): self.y2(); self.y3(); self.nState = 0

# ""   "News"
class PNSpider:
    def __init__(self, name):
        self.nState = 0; self.bIfLoop = True; self.site_name = name; self.page = 1;
        self.p_mainFSM = n_sleep;
    def x1(self): return self.page < 4
    def y1(self):
        self.bIfLoop = False; automaton.append(n_sleep);
        n_sleep.p_mainFSM = news
        automaton[-1].bIfLoop = True;
        automaton[-1].nState = 0
    def y2(self): print(self.site_name, self.page)
    def y3(self): self.page += 1
    def y4(self): self.page = 1
    def loop(self):
        if (self.x1() and self.nState == 0):  self.y1(); self.nState = 1
        elif (not self.x1()  and self.nState == 0): self.y1(); self.y4(); self.nState = 33
        elif (self.nState == 1): self.y2(); self.y3(); self.nState = 0

#    "Forum"
class PFSpider:
    def __init__(self, name):
        self.nState = 0; self.bIfLoop = True; self.site_name = name; self.page = 1;
        self.p_mainFSM = f_sleep;
    def x1(self): return self.page < 4
    def y1(self):
        self.bIfLoop = False; automaton.append(f_sleep);
        f_sleep.p_mainFSM = forum
        automaton[-1].bIfLoop = True;
        automaton[-1].nState = 0
    def y2(self): print(self.site_name, self.page)
    def y3(self): self.page += 1
    def y4(self): self.page = 1
    def loop(self):
        if (self.x1() and self.nState == 0):  self.y1(); self.nState = 1
        elif (not self.x1()  and self.nState == 0): self.y1(); self.y4(); self.nState = 33
        elif (self.nState == 1): self.y2(); self.y3(); self.nState = 0

# 
b_sleep = PSleep(1, 0)
n_sleep = PSleep(1, 0)
f_sleep = PSleep(1, 0)
# ""
blog = PBSpider("Blog")
news = PNSpider("News")
forum = PFSpider("Forum")
#    
automaton = []
automaton.append(blog);
automaton.append(news);
automaton.append(forum);
start = time.time()
#   ( event_loop)
while True:
    ind = 0;
    while True:
        while ind < len(automaton):
            if automaton[ind].nState == 4:
                automaton[ind].p_mainFSM.bIfLoop = True
                automaton.pop(ind)
                ind -=1
            elif automaton[ind].bIfLoop:
                automaton[ind].loop()
            elif automaton[ind].nState == 33:
                print("{:.2F}".format(time.time() - start))
                exit()
            ind += 1
        ind = 0




وإليكم نتيجة الرحلات الجوية التلقائية:



أخبار 1

المنتدى 1

مدونة 1

مدونة 2

أخبار 2

منتدى 2

أخبار 3

منتدى 3

مدونة 3

3.00



ولكن - لنناقش. كانت الزيادة في حجم الشفرة بسبب مشاكل في المؤشرات في بايثون. نتيجة لذلك ، كان علي إنشاء فصل دراسي لكل صفحة ، مما أدى إلى مضاعفة الرمز ثلاث مرات. لذلك ، من الأصح أن لا نتحدث عن 15 ، بل عن زيادة خمسة أضعاف في الحجم. قد يتمكن "طيار بايثون" الأكثر مهارة في البرمجة من القضاء على هذا القصور.



لكن السبب الرئيسي لا يزال غير المؤشرات. كود C ++ الموضح أدناه ، مع الحرية الكاملة للعمل مع المؤشرات ، يحتوي على عدد أكبر من الأسطر لكل فصل. والسبب هو النموذج الحسابي المستخدم ولغة وصفه ومقاربات تطبيق الخوارزميات المبنية عليه. الشكل: يوضح الشكل 1 نموذجًا تقليديًا للطائرة العنكبوتية في شكل مخطط كتلة ونموذج مدفع رشاش. يمكنك أن ترى أن هذه نماذج مختلفة ظاهريًا وفي الجودة ، على الرغم من أنها تسمح بتحولات مكافئة. تحتوي الأوتوماتا على حالات ، لكن المخططات الكتل لا تحتوي حتى على أي أثر لها. تعمل Automata ، بحكم التعريف ، في وقت منفصل ، ولا تحلم المخططات الكتل بهذا الأمر. كل هذا يفرض التزامات معينة على تنفيذ النموذج.



إن غياب مفهوم الوقت المنفصل هو جوهر مشاكل نموذج برمجة مخطط الكتلة الحالي ، والذي يجب أن ينفذ ، بالمعنى الدقيق للكلمة ، غير قابل للتحقيق بالنسبة له ، أي عمليات موازية. تذكر أن شبكة التشغيل الآلي ، كنموذج للعمليات المتوازية (وكذلك غير المتزامنة) ، هي حالتها الطبيعية.



الشكل: 1. نماذج تلقائية ومخطط كتلة لطائرة عنكبوتية
image



ولكن حتى على مستوى العملية المنفصلة ، فإن النماذج لها اختلافات يتم إسقاطها على لغة النموذج وتطبيقه. بفضل هذه الصفات ، ابتكر المدافعون عن مخطط كتلة متسق تركيبات لغوية ، سواء بشكل صريح أو ضمني ، تجعل من الممكن وصفها بشكل مضغوط للغاية. خذ نفس الشيء لـ loop أو على الأقل تسلسلًا ضمنيًا لتنفيذ المشغل (الإجراءات y1 ، y2 ، y3).



بالنسبة للمخطط الانسيابي ، يمكنك سرد الإجراءات في مربع واحد دون أي مشاكل وهذا لن يغير الطبيعة التسلسلية لعملهم. إذا استبدل الأوتوماتون الانتقالات في الحالات s2 و s3 بدورة في الحالة s1 ، مع وضع علامة على القوس بنفس الإجراءات ، فإن معنى الخوارزمية سيتغير ، منذ ذلك الحين سيقدم التوازي لعملها. نظرًا لأنه يجب تنفيذ الإجراءات المذكورة أعلاه بشكل متسلسل بدقة ، فقد حدد هذا مسبقًا مظهر نموذج التشغيل الآلي (انظر الشكل 1). الإنسان المحدود هو نموذج لا يسمح "بالتفكير المزدوج".



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



لكن عد إلى موضوع المقال وصوره. أردنا "طائرتنا" وحصلنا عليها! الرحلات الجوية ، أو بشكل أكثر دقة ، نتائجها المرئية ، مختلفة نوعًا ما في المظهر ، ولكن لا يمكن تمييزها تمامًا في طبيعتها. يمكن تفسيرها بطريقة يتم فيها اختيار الأعلام وتسجيلها في البروتوكول بترتيب مختلف ، لكن "الطائرات" نفسها طارت كما ينبغي ، أي في وقت واحد وألقوا أعلامهم في وقت واحد. وبأي ترتيب تم تسجيلها - الحالة ، كما يقولون ، هي العاشرة. يتم تحقيق الشيء الرئيسي والوفاء به: تسلسل وأوقات إطلاقها تتوافق مع برنامج الرحلة



يمكن تقصير الرمز. لذلك ، على ما يبدو ، يمكنك أن تقتصر على رمز فئة واحدة فقط. يمكنك أيضًا إخفاء رمز حلقة الحدث. إذا قمت ، في نفس الوقت ، في الكود المصدري بفتح رمز حجرة المحرك ، والذي كان مخفيًا خلف asyncio وينتظر المشغلين ، فلن يكون حجم رمز التشغيل الآلي مخيفًا على الأرجح.



3. حول مشاكل تنفيذ التشغيل الآلي في بايثون



دعونا نتناول المزيد من التفاصيل حول المشاكل التي أدت إلى ظهور الكود التلقائي. آخر شيء يجب إخفاءه هناك يبدو وحشيًا حتى الآن مقارنةً بالشفرة المصدرية. لكن دعونا نلاحظ أن أول طائرة من طراز Mozhaisky كانت بعيدة كل البعد عن التشابه مع "التجفيف" الحديث ، وأن السيارات الأولى لم تختلف للأفضل عن أي من نظيراتها الحديثة. اسمحوا لي أن أؤكد أن مشاكل الكود الآلي المقدم مرتبطة بشكل كبير بفهمي الحالي للغة بايثون ، وربما بدرجة أقل بقدرات اللغة نفسها.



ومع ذلك ، فإن المشكلة الأولى تتعلق بلغة وصف نموذج التشغيل الآلي. في C ++ يتم حلها عن طريق اللغة. لا أرى مثل هذه الاحتمالات في بايثون. لسوء الحظ ، كما يقولون الآن أحيانًا ، من كلمة على الإطلاق. لذلك ، تم اعتماد طريقة تنفيذ التشغيل الآلي على أساس مشغلي التحكم للغة if-elif-else كأساس. بالإضافة إلى ذلك ، نتذكر أنه في CPSU (a) ، بالإضافة إلى الأوتوماتا نفسها ، تم تقديم ذاكرة الظل ومساحات التشغيل الآلي من أجل التنفيذ الكامل للتوازي. بدون ذلك ، تكون إمكانيات برمجة التشغيل الآلي محدودة للغاية ومن نواحٍ عديدة تكون أقل شأناً.



المشكلة التالية التي ذكرناها بالفعل هي المؤشرات. لا توجد مشاكل معهم في C ++. في إطار CPSU (أ) ، وفقًا لنموذج OOP ، تم إنشاء فئة آلية أساسية ، يتم من خلالها إنشاء فئات التشغيل الآلي المطبقة ، ويمكن أن تكون معلماتها ليست مؤشرات فحسب ، بل حتى عناوينها. كل هذا يجعل من الممكن ببساطة وبشكل مضغوط وفعال للغاية وصف وتنفيذ أي مهمة تتضمن العديد من العمليات التفاعلية المتوازية.



فيما يلي رمز فئات C ++ automata المكافئة للمثال المدروس. رمز التأخير في القائمة 4 يعادل السطر الذي ينتظر asyncio.sleep (1) في القائمة 1. في شكل رسومي ، يتوافق مع نموذج FAwaitSleep التلقائي في الشكل 1. 1. يمكن اعتبار هذا الجهاز الآلي وفقطه غير متزامن ولن يؤدي إلى إبطاء التدفق الحسابي. النوم في نفس الشكل يتوافق مع عامل السكون المعتاد (). إنه أبسط ، ولكنه مضمون لتدمير نموذج الوقت المنفصل بسبب عمل y1 الذي يتسبب في التأخير المتسلسل المعتاد. وهذا لم يعد مفيدًا لأي شيء.



سرد 4. رمز تأخير غير متزامن
//  (  )
#include "lfsaappl.h"
#include <QTime>

class FAwaitSleep :
    public LFsaAppl
{
public:
    FAwaitSleep(int n);
protected:
    int x1();
    QTime time;
    int nAwaitSleep;
};

#include "stdafx.h"
#include "FAwaitSleep.h"

static LArc TBL_AwaitSleep[] = {
    LArc("s1",		"s1","x1",  "--"),			//
    LArc("s1",		"00","^x1",	"--"),			//
    LArc()
};

FAwaitSleep::FAwaitSleep(int n):
    LFsaAppl(TBL_AwaitSleep, "FAwaitSleep")
{
    nAwaitSleep = n; time.start();
}

int FAwaitSleep::x1() { return time.elapsed() < nAwaitSleep; }




يظهر كود C ++ للطائرة العنكبوتية في القائمة 5. هذا الرمز أكثر ملاءمة لنموذجها من الرسم التخطيطي للكتلة لشفرة Python. خاصة إذا قارنا جدول الانتقال الخاص بالآلة الآلية ومظهر الرسم البياني الآلي. إنها ببساطة أشكال مختلفة لوصف نفس المفهوم المجرد - الإنسان الآلي. يُظهر أيضًا كيفية تمرير مؤشر إلى الفئة الأصلية عند إنشاء تأخير (راجع استدعاء طريقة FCall في النشاط y1)



القائمة 5. رمز لطائرة عنكبوتية تحاكي قراءة صفحات الموقع
// "".   
#include "lfsaappl.h"

class FAwaitSleep;
class FSpider :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FSpider(nameFsa); }
    bool FCreationOfLinksForVariables() override;
    FSpider(string strNam);
    virtual ~FSpider(void);
    CVar *pVarStrSiteName;		//  
    FAwaitSleep *pFAwaitSleep{nullptr};
protected:
    int x1(); void y1(); void y2(); void y3(); void y4();
    int page{1};
};

#include "stdafx.h"
#include "FSpider.h"
#include "FSleep.h"
#include "FAwaitSleep.h"
#include <QDebug>

static LArc TBL_Spider[] = {
    LArc("st","s1","--","--"),		
    LArc("s1","s2","x1","y1"),  // x1- <. ; y1-;
    LArc("s2","s3","--","y2"),  // y2-   ;
    LArc("s3","s1","--","y3"),  // y3-   
    LArc("s1","st","^x1","y4"), // y4-   
    LArc()
};
FSpider::FSpider(string strNam):
    LFsaAppl(TBL_Spider, strNam)
{ }
FSpider::~FSpider(void) { if (pFAwaitSleep) delete pFAwaitSleep; }

bool FSpider::FCreationOfLinksForVariables() {
    pVarStrSiteName = CreateLocVar("strSiteName", CLocVar::vtString, "name of site");
    return true;
}
//      ?
int FSpider::x1() { return page < 4; }
// create delay - pure sleep (synchronous function) or await sleep (asynchronous function)
void FSpider::y1() {
    //sleep(1000);
    // await sleep (asynchronous function)
    if (pFAwaitSleep) delete pFAwaitSleep;
    pFAwaitSleep = new FAwaitSleep(1000);
    pFAwaitSleep->FCall(this);
}
void FSpider::y2() {
#ifdef QT_DEBUG
    string str = pVarStrSiteName->strGetDataSrc();
    printf("%s%d", str.c_str(), page);
    qDebug()<<str.c_str()<<page;
#endif
}
void FSpider::y3() { page++; }
void FSpider::y4() { page = 1; }




لا يوجد رمز ينفذ وظائف ما يسمى حلقة الحدث. ببساطة ليست هناك حاجة لذلك. يتم تنفيذ وظائفها من خلال جوهر بيئة CPSU (a). يخلق كائنات ويدير تنفيذها المتوازي في وقت منفصل.



4 - نتائج



الإيجاز ليس دائمًا أخت الموهبة ، وأحيانًا يكون أيضًا علامة على ارتباط اللسان. صحيح أنه من الصعب تمييز أحدهما عن الآخر دفعة واحدة. غالبًا ما يكون كود Python أقصر من كود C ++. لكن هذا نموذجي للحالات البسيطة. كلما كان الحل أكثر تعقيدًا ، قل هذا الاختلاف. في النهاية ، يتم تحديد مدى تعقيد الحل من خلال قدرات النموذج. نموذج آلي أقوى بكثير من مخطط الكتلة.



إن الأوتوماتا والتوازي ، أولاً وقبل كل شيء ، وسيلة فعالة للغاية لحل مشاكل التعقيد ، ومحاربتها ، وليست وسيلة لزيادة سرعة البرنامج. نظرًا لأن كل هذا هو نموذج آلي ، فمن الصعب تنفيذ التوازي في Python ، لذلك ، على الرغم من كل شرائحها وبطارياتها وأكثر من ذلك بكثير ، يصعب علي التأثير في اتجاهها. سأولي المزيد من الاهتمام لبيئة C ++ ، وليس إدخال مبرر للغاية لنفس coroutines فيها. هذا النموذج مؤقت والسبب في تنفيذه قسري إلى حد كبير. ماذا سنفعل بهذا "العكاز" عندما تحل مشكلة اختيار النموذج الموازي؟



لذلك ، آسف ، لكن تفضيلي لا يزال في جانب C ++. وإذا كنت تفكر في مجال اهتماماتي المهنية - الأنظمة الصناعية لما يسمى بالزمن الصعب "المخيف" ، فلن يكون لدي خيار على هذا النحو. نعم ، نوع من البيئة ، يمكن إنشاء نوع من الخدمة باستخدام Python. إنه ملائم وسريع ، وهناك العديد من النماذج الأولية ، إلخ. إلخ لكن جوهر الحل ، ونموذجه الموازي ، ومنطق العمليات نفسها ، لا لبس فيه ، C ++ ، آلية بشكل لا لبس فيه. الأوتوماتا هنا ، بالطبع ، هي أكثر أهمية وحكمًا. لكن ليس coroutines :)



بالإضافة إلى ... شاهد الفيديو [2] ، مع الانتباه إلى تنفيذ نموذج الصاروخ. يروي الفيديو عنها ، بدءًا من حوالي الثانية عشرة دقيقة. احترام المحاضر لاستخدام الماكينات :) ولعلاج حلوى حل آخر من [3]... إنه بروح البرمجة غير المتزامنة وعدم التزامن. في الواقع ، بدأ كل شيء بهذا المثال - تنفيذ أتمتة متداخلة في Python. هنا يكون عمق التعشيش أكبر مما هو عليه في المثال المفصل أعلاه. تُظهر القائمة 6 الكود المصدري ونظيره الآلي في Python. في التين. 2 هو نموذج شرب الشاي الأوتوماتيكي ، والقائمة 7 توضح تنفيذ C ++ المكافئ لـ VKP (a). قارن ، حلل ، استخلص استنتاجات ، انتقد ...



قائمة 6. قراءة وشرب الشاي بشكل غير متزامن في بايثون
import asyncio
import time

# # Easy Python. Asyncio  python 3.7 https://www.youtube.com/watch?v=PaY-hiuE5iE
# # 10:10
# async def teatime():
#     await asyncio.sleep(1)
#     print('take a cap of tea')
#     await asyncio.sleep(1)
#
# async def read():
#     print('Reading for 1 hour...')
#     await teatime()
#     print('...reading for 1 hour...')
#
# if __name__ == '__main__':
#     asyncio.run(read())

class PSleep:
    def __init__(self, t, p_FSM): self.SetTime = t; self.nState = 0; self.bIfLoop = False; self.p_mainFSM = p_FSM
    def x1(self): return time.time() - self.t0 <= self.SetTime
    def y1(self): self.t0 = time.time()
    def loop(self):
        if (self.nState == 0): self.y1(); self.nState = 1
        elif (self.nState == 1):
            if (not self.x1()): self.nState = 4

class PTeaTime:
    def __init__(self, p_FSM): self.nState = 0; self.bIfLoop = False; self.p_mainFSM = p_FSM;
    def y1(self): self.bIfLoop = False; automaton.append(sl); automaton[-1].bIfLoop = True; automaton[-1].nState = 0
    def y2(self): print('take a cap of tea')
    def loop(self):
        if (self.nState == 0):  self.y1(); self.nState = 1
        elif (self.nState == 1): self.y2(); self.nState = 2
        elif (self.nState == 2): self.y1(); self.nState = 3
        elif (self.nState == 3): self.nState = 4

class PRead:
    def __init__(self): self.nState = 0; self.bIfLoop = False;
    def y1(self): print('Reading for 1 hour...')
    def y2(self): self.bIfLoop = False; automaton.append(rt); automaton[-1].bIfLoop = True; automaton[-1].nState = 0
    def loop(self):
        if (self.nState == 0): self.y1(); self.nState = 1
        elif (self.nState == 1): self.y2(); self.nState = 2
        elif (self.nState == 2): self.y1(); self.nState = 33; self.bIfLoop = False

read = PRead()
rt = PTeaTime(read)
sl = PSleep(5, rt)
automaton = []
automaton.append(read); automaton[-1].bIfLoop = True
while True:
    ind = 0;
    while True:
        while ind < len(automaton):
            if automaton[ind].nState == 4:
                automaton[ind].p_mainFSM.bIfLoop = True
                automaton.pop(ind)
                ind -=1
            elif automaton[ind].bIfLoop:
                automaton[ind].loop()
            elif automaton[ind].nState == 33:
                exit()
            ind += 1
        ind = 0




الشكل: 2. نموذج آلي لشرب الشاي
image



قائمة 7. قراءة وشرب الشاي بشكل غير متزامن بلغة C ++
#include "lfsaappl.h"

class FRead :
    public LFsaAppl
{
public:
    LFsaAppl* Create(CVarFSA *pCVF) { Q_UNUSED(pCVF)return new FRead(nameFsa); }
    FRead(string strNam);
    virtual ~FRead(void);
protected:
    void y1(); void y2();  void y3();
    LFsaAppl *pFRealTime{nullptr};
};

#include "stdafx.h"
#include "FRead.h"
#include "FTeaTime.h"
#include <QDebug>

static LArc TBL_Read[] = {
    LArc("s1","s2","--","y1"),	// Reading for 1 hour...
    LArc("s2","s3","--","y2"),	// Call(TeaTime)
    LArc("s3","s4","--","y1"),	// Reading for 1 hour...
    LArc("s4","s5","--","y3"),	// sleep(5)
    LArc("s5","s1","--","--"),	//
    LArc()
};
FRead::FRead(string strNam):
    LFsaAppl(TBL_Read, strNam)
{ }
FRead::~FRead(void) { if (pFRealTime) delete pFRealTime; }

void FRead::y1() {
#ifdef QT_DEBUG
    qDebug()<<"Reading for 1 hour...";
#endif
}
void FRead::y2() {
    if (pFRealTime) delete pFRealTime;
    pFRealTime = new FTeaTime("TeaTime");
    pFRealTime->FCall(this);
}
void FRead::y3() { FCreateDelay(5000); }


#include "lfsaappl.h"

class FTeaTime :
    public LFsaAppl
{
public:
    FTeaTime(string strNam);
protected:
    void y1(); void y2();
};
#include "stdafx.h"
#include "FTeaTime.h"
#include <QDebug>
#include "./LSYSLIB/FDelay.h"

static LArc TBL_TeaTime[] = {
    LArc("s1",	"s2","--","y1"),// sleep(1)
    LArc("s2",	"s3","--","y2"),// take a cap of tea
    LArc("s3",	"s4","--","y1"),// sleep(1)
    LArc("s4",	"00","--","--"),//
    LArc()
};

FTeaTime::FTeaTime(string strNam):
    LFsaAppl(TBL_TeaTime, strNam)
{ }

void FTeaTime::y1() { FCreateDelay(2000); }
void FTeaTime::y2() {
#ifdef QT_DEBUG
    qDebug()<<"take a cap of tea";
#endif
}




ملاحظة



بالفعل بعد كتابة المقال ، بعد قراءة ترجمة مقال يرين دياز [4] ، تعرفت على نظرة أخرى مثيرة للاهتمام إلى حد ما ومعجبة إلى حد ما في coroutines بشكل عام و asyncio بشكل خاص. على الرغم من هذه الحقيقة وغيرها مثلها ، فإننا لا نزال "نذهب في الاتجاه الآخر" :) أنا أتفق فقط على شيء واحد مع روب بايك ، وهو أن "التزامن ليس متوازيًا". قد يقول المرء أنه أكثر صرامة ، ليس له علاقة بالتوازي على الإطلاق. ومن الجدير بالذكر أن ترجمة Google ترجمت هذه العبارة على أنها "التوازي ليس التوازي." الرجل المسمى جوجل خاطئ بالتأكيد. لكن أحدا أقنعه بذلك؟ :)



الأدب



  1. Shultais Education. 1. . [ ], : www.youtube.com/watch?v=BmOjeVM0w1U&list=PLJcqk6mrJtxCo_KqHV2rM2_a3Z8qoE5Gk, . . . ( 01.08.2020).
  2. Computer Science Center. 9. async / await ( Python). [ ], : www.youtube.com/watch?v=x6JZmBK2I8Y, . . . ( 13.07.2020).
  3. Easy Python. Asyncio python 3.7. [ ], : www.youtube.com/watch?v=PaY-hiuE5iE, . . . ( 01.08.2020).
  4. ييراي دياز. Asyncio لمطور بايثون الممارس. [مورد إلكتروني] ، وضع الوصول: www.youtube.com/watch؟v=PaY-hiuE5iE ، مجانًا. لغة. الروسية (تاريخ العلاج 08/01/2020).



All Articles