Eine Welt ohne Coroutinen. Krücken für den Programmierer - asyncio

1. Einleitung



Wer fliegen gelernt hat, kriecht nicht mehr. Es sollte aber auch keine Arroganz gegenüber jemandem geben, der grundsätzlich "nicht fliegen kann". Beides ist ganz normal. Beide sind respektiert und ehrenwert. Für eine Person ist dies wie die Berufswahl: Sie sind herkömmlicherweise entweder Pilot oder Fahrer. Für dieselben Tiere ist es dasselbe - Sie sind entweder ein Adler oder ein Wolf, d. H. Entweder du fliegst oder du rennst (renn weg) Aber nur der Mensch in seinen Konzepten, Kategorien, Einstellungen und Gedanken stattete die Charaktere mit Eigenschaften aus und entwickelte seine eigene Einstellung zu ihnen. Richtig, mit Nuancen. Also, nein, wahrscheinlich ist es ehrenhafter und romantischer als der Beruf eines Piloten, aber versuchen Sie, einen Trucker oder Flugzeugdesigner davon zu überzeugen ?! Und hier ist es schwer zu argumentieren: Es gibt schon jetzt viele Astronauten, aber es gibt immer noch keine zweite Königin!



Wir sind Programmierer. Vielleicht in unterschiedlichem Maße, aber einige - sicher. Ich meine, wir sind anders und können auch anders denken. Die Aussage, dass ein Programmierer nur konsequent denkt, ist ebenso einseitig, schädlich und sogar blasphemisch wie die Tatsache, dass eine Person nur läuft. Er manchmal - und fliegt. Einige, wie Piloten, tun dies ziemlich regelmäßig, andere, wie Astronauten, sogar monatelang und ununterbrochen. Die Idee des konsequenten Denkens verringert die menschlichen Fähigkeiten. Irgendwann und für eine Weile kann man es sogar glauben, aber "immer noch dreht es sich" - hier geht es um die Tatsache, dass das Leben früher oder später seinen Tribut fordern wird.



Asyncio in Python ist eine Software-Krücke, die im übertragenen Sinne den Flug des falschen parallelen Denkens nachahmt. Eine Art Hüpfen mit wehenden Händen. Es sieht manchmal lustig und ungeschickt aus. In einer bestimmten Situation ist dies zwar auch ein Ausweg: Sie können einfach die Pfütze überqueren und sich schmutzig machen, aber wenn die Kraft es zulässt, ist es besser, darüber zu springen. Aber vielleicht fehlt den Programmierern die Kraft?



Versuchen wir, die auferlegten "Software-Krücken" zu verwerfen und über die Software-Routine zu steigen. Und sei es kein Sprung oder vielleicht nicht so hoch und lang, aber dennoch, besonders im Vergleich zu Krücken, ein Flug. Immerhin haben Mozhaisky Alexander Fedorovich (nicht zu verwechseln mit dem Mozhaisky-Stadtgericht der Region Moskau) oder dieselben Gebrüder Wright zum ersten Mal mehrere hundert Meter in der Luft überwunden. Ja, und Tests moderner Flugzeuge beginnen mit einem Lauf und einer kurzfristigen Trennung von der Landebahn.



2. Ein sehr einfaches Beispiel mit Asyncio



Wir beginnen mit dem Fliegen in Python. Das Flugprogramm ist einfach. Es gibt Flugzeuge (die jedoch in der Originalversion der Bilder von Spinnen siehe [1] ) mit Namen auf den Rümpfen "Blog", "News", "Forum". Sie heben gleichzeitig ab. Jeder muss in einer bestimmten Zeit ein Segment des Pfades fliegen und beispielsweise eine Flagge mit der Nummer des abgedeckten Segments werfen. Dies muss dreimal durchgeführt werden. Und erst dann landen.



In Python wird ein Modell dieses Verhaltens beschrieben und anschließend durch den Code in Listing 1 simuliert.



Listing 1. Python-Code für Spinnenflugzeuge
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))




Die Simulationsergebnisse für einen solchen "Flug" lauten wie folgt:



Blog 1

Nachrichten 1

Forum 1

Blog 2

Nachrichten 2

Forum 2

Blog 3

Nachrichten 3

Forum 3

3.00



Warum dies im Video [1] so ausführlich erklärt wird. Aber wir Leute mit Fantasie und dem gleichzeitigen (je nach Szenario - asynchronen) Flug unserer drei "Flugzeuge" ohne Verwendung von Asyncio werden uns auf andere Weise präsentieren - auf der Basis von automatischen Modellen. Zum Beispiel zeigt Listing 2 den Code für eine Automatenverzögerung, ein Analogon der Async-Verzögerung vom Asyncio-Modul, dargestellt durch die Zeile, die in Listing 1 auf asyncio.sleep (1) wartet .



Listing 2. Automatischer Verzögerungscode in Python
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




Der Verzögerungswert und ein Zeiger auf das Objekt, das das Verzögerungsobjekt erstellt hat, werden durch den Klassenkonstruktor übergeben. Der Zeiger wird von der Prozesssteuerungsfunktion benötigt, die nach dem Entfernen der Verzögerung den übergeordneten Prozess fortsetzt, der beim Erstellen gestoppt wurde.



Listing 3 zeigt das automatische Gegenstück zur asynchronen Spinnenebene (siehe auch Listing 1). Es ist sehr wahrscheinlich, dass ein Python-Programmier-Ass selbst im größten Albtraum nicht davon träumen wird! Der Quellcode von vier Zeilen ist 15-mal gewachsen! Ist dies nicht ein Grund zur Bewunderung für typischen Python-Code im Allgemeinen und Asycio im Besonderen oder zumindest ein Beweis für den Vorteil der "Coroutine-Technologie" gegenüber der automatischen Programmierung?



Listing 3. Code für eine Automatenspinne in Python
# ""   "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




Und hier ist das Ergebnis von automatischen Flügen:



Nachrichten 1

Forum 1

Blog 1

Blog 2

Nachrichten 2

Forum 2

Nachrichten 3

Forum 3

Blog 3

3.00



Aber - lassen Sie uns diskutieren. Die Zunahme der Codegröße war auf Probleme mit Zeigern in Python zurückzuführen. Infolgedessen musste ich für jede Seite eine Klasse erstellen, die den Code verdreifachte. Daher ist es richtiger, nicht von 15 zu sprechen, sondern von einer Verfünffachung der Lautstärke. Ein geschickterer "Python-Pilot" in der Programmierung kann diesen Mangel möglicherweise sogar beseitigen.



Aber der Hauptgrund sind immer noch keine Hinweise. Der unten gezeigte C ++ - Code mit der vollständigen Freiheit, mit Zeigern zu arbeiten, enthält noch mehr Zeilen pro Klasse. Der Grund ist das verwendete Rechenmodell, die Sprache seiner Beschreibung und Ansätze zur Implementierung darauf basierender Algorithmen. Zahl: 1 zeigt ein herkömmliches Spinnenebenenmodell in Blockdiagrammform und ein Maschinengewehrmodell. Sie können sehen, dass dies äußerlich und qualitativ unterschiedliche Modelle sind, obwohl sie äquivalente Transformationen ermöglichen. Automaten haben Zustände, aber Blockdiagramme haben nicht einmal eine Spur von ihnen. Automaten arbeiten per Definition in diskreter Zeit, und Blockdiagramme träumen nicht einmal davon. All dies bringt bestimmte Verpflichtungen für die Umsetzung des Modells mit sich.



Das Fehlen des Konzepts der diskreten Zeit ist die Essenz der Probleme des bestehenden Blockdiagramm-Programmiermodells, das genau genommen erkennen muss, was für es nicht realisierbar ist, d. H. parallele Prozesse. Denken Sie daran, dass für Automaten ein Automaten-Netzwerk als Modell für parallele (sowie asynchrone) Prozesse ihr natürlicher Zustand ist.



Zahl: 1. Automatische und Blockdiagrammmodelle einer Spinnenebene
image



Aber auch auf der Ebene eines separaten Prozesses weisen Modelle Unterschiede auf, die auf die Sprache und Implementierung des Modells projiziert werden. Aufgrund dieser Eigenschaften haben die Apologeten für ein konsistentes Blockdiagramm Sprachkonstrukte erstellt, die es entweder explizit oder implizit ermöglichen, es sehr kompakt zu beschreiben. Nehmen Sie dasselbe für die Schleife oder zumindest eine implizit implizierte Folge der Operatorausführung (Aktionen y1, y2, y3).



Bei einem Flussdiagramm können Sie die Aktionen problemlos in einem Feld auflisten. Dies ändert nichts an der Reihenfolge ihrer Arbeit. Wenn der Automat die Übergänge in den Zuständen s2, s3 durch einen Zyklus im Zustand s1 ersetzt und den Bogen mit denselben Aktionen markiert, ändert sich die Bedeutung des Algorithmus seitdem wird Parallelität in seine Arbeit einführen. Da die obigen Aktionen streng nacheinander ausgeführt werden müssen, hat dies das Erscheinungsbild des Automatenmodells vorbestimmt (siehe Abb. 1). Ein endlicher Automat ist ein Modell, das kein "Doppeldenken" zulässt.



Der Mangel an diskreter Zeit in Blockdiagrammen war ihr Vorteil. Aber jetzt ist dies ihr Hauptnachteil geworden. Es beeinflusst, wie es nicht blasphemisch erscheinen mag, und die Denkweise. Sequentielle Sprachen rechtfertigen das sequentielle Denken von Programmierern, indem sie ihnen etwas anderes verweigern - parallel. Dies ist es, was die Konstruktionen der vorhandenen asynchronen Programmierung rechtfertigt und die Menge und Funktionalität von Operatoren desselben asyncio-Pakets darstellt. Und genau dieser Ansatz, so wird argumentiert, ermöglicht es Programmierern, die mit sequentiellen Programmen vertraut sind, asynchron (fast parallel) zu werden.



Aber zurück zum Thema des Artikels und seinen Bildern. Wir wollten unser "Flugzeug" und wir haben es bekommen! Flüge, genauer gesagt ihre sichtbaren Ergebnisse, sehen etwas anders aus, sind aber in der Natur völlig ununterscheidbar. Sie können so interpretiert werden, dass die Flaggen ausgewählt und in einer anderen Reihenfolge im Protokoll aufgezeichnet wurden, aber die "Flugzeuge" selbst flogen, wie sie sollten, d. H. gleichzeitig und gleichzeitig warfen sie ihre Fahnen aus. Und in welcher Reihenfolge sie aufgenommen wurden - der Fall ist, wie sie sagen, der zehnte. Die Hauptsache ist realisiert und erfüllt: Die Reihenfolge und die Zeiten ihrer Veröffentlichung entsprechen dem Flugprogramm



Der Code kann gekürzt werden. Anscheinend können Sie sich also auf den Code nur einer Klasse beschränken. Sie können den Ereignisschleifencode auch ausblenden. Wenn Sie gleichzeitig im Quellcode den Motorraumcode öffnen, der sich hinter dem Asyncio verbirgt, und auf Bediener warten, ist die Menge des Automatencodes höchstwahrscheinlich nicht so unheimlich.



3. Zu den Problemen bei der Implementierung von Automaten in Python



Lassen Sie uns näher auf die Probleme eingehen, die zum Auftreten des automatischen Codes geführt haben. Das Letzte, was sich dort versteckt, sieht im Vergleich zum Quellcode bisher ungeheuerlich aus. Beachten wir jedoch, dass das erste Flugzeug von Mozhaisky dem modernen "Trocknen" bei weitem nicht ähnlich war und sich die ersten Autos nicht zum Besseren von ihren modernen Gegenstücken unterschieden. Lassen Sie mich betonen, dass die Probleme des vorgestellten Automatencodes weitgehend mit meinem derzeitigen Verständnis der Python-Sprache und möglicherweise in geringerem Maße mit den Fähigkeiten der Sprache selbst zusammenhängen.



Das erste Problem hängt jedoch mit der Beschreibungssprache des Automatenmodells zusammen. In C ++ wird es mittels der Sprache gelöst. Ich sehe solche Möglichkeiten in Python nicht. Leider, wie sie jetzt manchmal sagen, vom Wort überhaupt. Daher wurde die Methode zur Implementierung von Automaten zugrunde gelegt, die auf Steuerungsoperatoren der if-elif-else-Sprache basieren. Darüber hinaus erinnern wir uns, dass in der KPdSU (a) zusätzlich zu den Automaten selbst Schattenspeicher und Automatenräume eingeführt wurden, um die Parallelität vollständig zu implementieren. Ohne dies sind die Möglichkeiten der Automatenprogrammierung sehr begrenzt und in vielerlei Hinsicht minderwertig.



Das nächste Problem, das wir bereits erwähnt haben, sind Zeiger. Es gibt keine Probleme mit ihnen in C ++. Im Rahmen der KPdSU (a) wurde gemäß dem OOP-Paradigma eine grundlegende Automatenklasse erstellt, aus der angewandte Automatenklassen generiert werden und deren Parameter nicht nur Zeiger, sondern auch deren Adressen sein können. All dies ermöglicht es, jede Aufgabe, die viele parallele Interaktionsprozesse umfasst, einfach, kompakt und sehr effizient zu beschreiben und umzusetzen.



Das Folgende ist der Code von C ++ - Automatenklassen, der dem betrachteten Beispiel entspricht. Der Verzögerungscode in Listing 4 entspricht der Zeile, die auf asyncio.sleep (1) in Listing 1 wartet. In grafischer Form entspricht er dem FAwaitSleep-Automatenmodell in Abbildung 1. 1. Ein solcher und nur ein solcher Automat kann als asynchron betrachtet werden und verlangsamt den Rechenfluss nicht. FSleep in derselben Abbildung entspricht dem üblichen sleep () -Operator. Es ist einfacher, aber garantiert, das diskrete Zeitmodell aufgrund der Wirkung von y1 zu zerstören, die die übliche sequentielle Verzögerung verursacht. Und das ist für nichts mehr gut.



Listing 4. Asynchroner Verzögerungscode
//  (  )
#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; }




Der C ++ - Code für die Spinnenebene ist in Listing 5 dargestellt. Dieser Code passt viel besser zu seinem Modell als das Blockdiagramm des Python-Codes. Besonders wenn wir die Übergangstabelle des Automaten und das Erscheinungsbild des Automatendiagramms vergleichen. Sie sind einfach verschiedene Formen der Beschreibung desselben abstrakten Konzepts - eines Automaten. Es zeigt auch, wie beim Erstellen einer Verzögerung ein Zeiger auf die übergeordnete Klasse übergeben wird (siehe Aufruf der FCall-Methode in Aktivität y1).



Listing 5. Code für eine Spinnenebene, die das Lesen von Site-Seiten simuliert
// "".   
#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; }




Es gibt keinen Code, der die Funktionen der sogenannten Ereignisschleife implementiert. Es besteht einfach keine Notwendigkeit dafür. Seine Funktionen werden vom Kern der CPSU (a) -Umgebung ausgeführt. Es erstellt Objekte und verwaltet deren parallele Ausführung in diskreter Zeit.



4. Schlussfolgerung



Kürze ist nicht immer die Schwester des Talents, und manchmal ist sie auch ein Zeichen von Zungenbindung. Es ist zwar schwierig, einen auf einmal voneinander zu unterscheiden. Python-Code ist häufig kürzer als C ++ - Code. Dies ist jedoch typisch für einfache Fälle. Je komplexer die Lösung ist, desto geringer ist dieser Unterschied. Letztendlich wird sogar die Komplexität der Lösung durch die Fähigkeiten des Modells bestimmt. Ein Automatenmodell ist viel leistungsfähiger als ein Blockdiagramm.



Automaten und Parallelisierung sind in erster Linie sehr effektive Mittel, um Komplexitätsprobleme zu lösen, sie zu bekämpfen, und weniger ein Mittel, um die Geschwindigkeit eines Programms zu erhöhen. Da dies alles ein Automatenmodell ist, ist es schwierig, Parallelität in Python zu implementieren. Trotz all seiner Chips, Batterien und vielem mehr fällt es mir schwer, in seine Richtung zu schwanken. Ich würde der C ++ - Umgebung mehr Aufmerksamkeit schenken und die Einführung derselben Coroutinen nicht sehr gerechtfertigt. Dieses Modell ist vorübergehend und der Grund für seine Implementierung ist weitgehend erzwungen. Und was machen wir mit dieser "Krücke", wenn das Problem der Wahl eines Parallelmodells gelöst ist?



Daher tut mir leid, aber meine Präferenz ist immer noch auf der C ++ - Seite. Und wenn Sie den Bereich meiner beruflichen Interessen betrachten - industrielle Systeme der sogenannten "gruseligen" harten Echtzeit, dann habe ich als solche keine Wahl. Ja, mit Python kann eine Umgebung oder ein Dienst erstellt werden. Es ist praktisch, es ist schnell, es gibt viele Prototypen usw. usw. Der Kern der Lösung, ihr paralleles Modell und die Logik der Prozesse selbst sind jedoch eindeutig C ++, eindeutig Automaten. Hier sind natürlich Automaten wichtiger und herrschen. Aber keine Coroutinen :)



Außerdem ... Sehen Sie sich das Video [2] an und achten Sie dabei auf die Implementierung des Raketenmodells. Über sie erzählt das Video ab etwa der 12. Minute. Respekt an den Dozenten für die Benutzung der Maschinen :) Und für einen süßen Leckerbissen eine andere Lösung aus [3]... Es ist im Geiste der asynchronen Programmierung und Asyncio. Eigentlich begann alles mit diesem Beispiel - der Implementierung verschachtelter Automaten in Python. Hier ist die Verschachtelungstiefe noch größer als im oben beschriebenen Beispiel. Listing 6 zeigt den Quellcode und sein Gegenstück zum Python-Automaten. In Abb. 2 ist das automatische Teetrinkmodell, und Listing 7 zeigt die entsprechende C ++ - Implementierung für VKP (a). Vergleichen, analysieren, Schlussfolgerungen ziehen, kritisieren ...



Listing 6. Asynchrones Lesen und Trinken von Tee in Python
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




Zahl: 2. Automatisches Modell des Teetrinkens
image



Listing 7. Asynchrones Lesen und Trinken von Tee in 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
}




PS



Bereits nach dem Schreiben des Artikels, nachdem ich die Übersetzung des Artikels von Yerain Diaz [4] gelesen hatte , lernte ich einen weiteren interessanten und eher bewundernden Blick auf Coroutinen im Allgemeinen und Asyncio im Besonderen kennen. Trotz dieser Tatsache und anderer, die es mögen, werden wir immer noch "den anderen Weg gehen" :) Ich stimme Rob Pike nur in einer Sache zu: "Parallelität ist kein Parallelismus". Wettbewerbsfähigkeit, könnte man sogar härter sagen, hat überhaupt nichts mit Parallelität zu tun. Und es ist bemerkenswert, dass Google Translate diesen Satz als "Parallelität ist keine Parallelität" übersetzt. Der Mann namens Google ist sicherlich falsch. Aber jemand hat ihn davon überzeugt? :) :)



Literatur



  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. Yeray Diaz. Asyncio für den praktizierenden Python-Entwickler. [Elektronische Ressource], Zugriffsmodus: www.youtube.com/watch?v=PaY-hiuE5iE , kostenlos. Sprache. Russisch (Datum der Behandlung 08/01/2020).



All Articles