Un monde sans coroutines. Béquilles pour le programmeur - Asyncio

1. Introduction



Celui qui a appris à voler ne rampera plus. Mais il ne devrait pas y avoir non plus d'arrogance envers quelqu'un qui «ne peut pas voler» en principe. Les deux sont tout à fait normaux. Les deux sont respectés et honorables. Pour une personne, c'est comme choisir un métier: vous êtes, par convention, soit pilote, soit chauffeur. Pour les mêmes animaux, c'est la même chose - vous êtes soit un aigle, soit un loup, c'est-à-dire Soit tu voles, soit tu cours (fuis) Mais seule une personne dans ses concepts, catégories, attitudes et pensées a doté les personnages de caractéristiques et a développé son attitude à leur égard. C'est vrai, avec des nuances. Alors, non, probablement, c'est plus honorable et romantique que le métier de pilote, mais essayez de convaincre un camionneur ou un concepteur d'avion de cela?! Et ici, c'est difficile à argumenter: il y a encore beaucoup d'astronautes, mais il n'y a toujours pas de deuxième reine!



Nous sommes des programmeurs. Peut-être à des degrés divers, mais certains - c'est sûr. Cela veut dire que nous sommes différents et que nous pouvons aussi penser de différentes manières. L'affirmation selon laquelle un programmeur ne pense que de manière cohérente est tout aussi unilatérale, nuisible et même blasphématoire, que le fait qu'une personne ne fait que courir. Il parfois - et vole. Certains, comme les pilotes, le font assez régulièrement, et certains, comme les astronautes, même pendant des mois et en continu. L'idée d'une pensée cohérente diminue la capacité humaine. À un moment donné et pendant un certain temps, vous pouvez même le croire, mais "ça tourne toujours" - c'est sur le fait que tôt ou tard la vie fera des ravages.



Asyncio en Python est une béquille logicielle qui imite, au sens figuré, le vol d'une pensée parallèle incorrecte. Une sorte de rebond avec des mains agitées. Cela a parfois l'air drôle et maladroit. Bien que dans une certaine situation, ce soit également une issue: vous pouvez simplement traverser la flaque d'eau et vous salir, mais si la force le permet, il vaut mieux sauter par-dessus. Mais peut-être que les programmeurs manquent de force?



Essayons de jeter les «béquilles logicielles» imposées et de planer au-dessus de la routine logicielle. Et que ce ne soit pas un saut, et peut-être pas si haut et long, mais quand même, surtout en comparaison avec des béquilles, un vol. Après tout, une fois que Mozhaisky Alexander Fedorovich (à ne pas confondre avec le tribunal municipal Mozhaisky de la région de Moscou;) ou les mêmes frères Wright ont surmonté pour la première fois plusieurs centaines de mètres dans les airs. Oui, et les tests des avions modernes commencent par une course et une séparation à court terme de la piste.



2. Un exemple très simple avec asyncio



Nous allons commencer par voler en Python. Le programme de vol est simple. Il existe des avions (qui, cependant, dans la version originale de l'imagerie des araignées, voir [1] ) avec des noms sur les fuselages "Blog", "News", "Forum". Ils décollent en même temps. Tout le monde doit voler un segment du chemin dans un certain temps et lancer, par exemple, un drapeau avec le numéro du segment couvert. Cela doit être fait trois fois. Et seulement alors atterrir.



En Python, un modèle de ce comportement est décrit puis simulé par le code du Listing 1.



Listing 1. Code Python pour les avions araignées
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))




Les résultats de la simulation pour un tel «vol» sont les suivants:



Blog 1

News 1

Forum 1

Blog 2

News 2

Forum 2

Blog 3

News 3

Forum 3

3.00



Pourquoi cela est expliqué en détail par la vidéo [1]. Mais nous les gars avec de l'imagination et le vol simultané (selon le scénario - asynchrone) de nos trois "avions" sans utiliser asyncio se présenteront d'une manière différente - sur la base de modèles automatiques. Par exemple, le listing 2 montre le code d'un délai d'automate, un analogue du délai async du module asyncio, représenté par la ligne await asyncio.sleep (1) dans le listing 1.



Listing 2. Code de délai automatique en 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




La valeur de délai et un pointeur vers l'objet qui a créé l'objet de délai sont transmis via le constructeur de classe. Le pointeur est requis par la fonction de contrôle de processus, qui, après avoir supprimé le délai, continuera le processus parent, qui a été arrêté lors de sa création.



Le listing 3 montre l'homologue de l'automate du plan d'araignée asynchrone (voir aussi le listing 1). Il est très probable qu'un as de la programmation Python n'en rêvera pas, même dans le plus cauchemar! Le code source de quatre lignes a été multiplié par 15! N'est-ce pas une raison d'admiration pour le code Python typique en général et asycio en particulier, ou, du moins, une preuve de l'avantage de la "technologie coroutine" sur la programmation d'automates?



Listing 3. Code pour une araignée automate en 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




Et voici le résultat des vols automatiques:



News 1

Forum 1

Blog 1

Blog 2

News 2

Forum 2

News 3

Forum 3

Blog 3

3.00



Mais - discutons. L'augmentation de la taille du code était due à des problèmes avec les pointeurs en Python. En conséquence, j'ai dû créer une classe pour chaque page, ce qui a triplé le code. Par conséquent, il est plus correct de parler non pas d'environ 15, mais d'environ cinq fois le volume. Un «pilote Python» plus habile en programmation peut même être en mesure d'éliminer cette lacune.



Mais la raison principale n'est toujours pas les pointeurs. Le code C ++ ci-dessous, avec une totale liberté de travailler avec des pointeurs, a encore plus de lignes par classe. La raison en est le modèle de calcul utilisé, le langage de sa description et les approches de la mise en œuvre d'algorithmes basés sur celui-ci. Figure: 1 montre un modèle d'avion d'araignée classique sous forme de schéma de principe et un modèle de mitrailleuse. Vous pouvez voir que extérieurement et en qualité, ce sont des modèles différents, bien qu'ils permettent des transformations équivalentes. Les automates ont des états, mais les diagrammes de blocs n'en ont même pas la trace. Les automates, par définition, fonctionnent en temps discret, et les schémas blocs n'en rêvent même pas. Tout cela impose certaines obligations sur la mise en œuvre du modèle.



L'absence du concept de temps discret est l'essence même des problèmes du modèle de programmation par blocs existant, qui, à proprement parler, doit réaliser ce qui lui est irréalisable, c'est-à-dire processus parallèles. Rappelons que pour les automates un réseau d'automates, en tant que modèle de processus parallèles (ainsi qu'asynchrones), est leur état naturel.



Figure: 1. Modèles automatiques et schémas de principe d'un avion-araignée
image



Mais même au niveau d'un processus séparé, les modèles présentent des différences qui sont projetées sur le langage et la mise en œuvre du modèle. En vertu de ces qualités, les apologistes d'un schéma de principe cohérent ont créé des constructions de langage qui, soit explicitement, soit implicitement, permettent de le décrire de manière très compacte. Prenons la même boucle for ou au moins une séquence implicitement implicite d'exécution d'opérateurs (actions y1, y2, y3).



Pour un organigramme, vous pouvez lister les actions dans une case sans aucun problème et cela ne changera pas la nature séquentielle de leur travail. Si l'automate remplace les transitions dans les états s2, s3 par un cycle dans l'état s1, marquant l'arc avec les mêmes actions, alors la signification de l'algorithme changera, puisque introduira le parallélisme dans ses travaux. Puisque les actions ci-dessus doivent être exécutées strictement séquentiellement, cela a prédéterminé l'apparence du modèle d'automate (voir Fig. 1). Un automate fini est un modèle qui ne permet pas la "double pensée".



Le manque de temps discret dans les schémas fonctionnels était leur avantage. Mais maintenant, c'est devenu leur principal inconvénient. Cela affecte, car cela peut ne pas sembler blasphématoire, et la façon de penser. Les langages séquentiels justifient la pensée séquentielle des programmeurs en leur refusant autre chose - parallèle. C'est ce qui justifie les constructions de la programmation asynchrone existante, présentant l'ensemble et la fonctionnalité des opérateurs d'un même package asyncio. Et c'est cette approche, soutient-on, qui permet aux programmeurs de transformer leurs programmes séquentiels familiers en programmes asynchrones (presque parallèles).



Mais revenons au sujet de l'article et de ses images. Nous voulions notre "avion" et nous l'avons eu! Les vols, ou plus précisément leurs résultats visibles, sont quelque peu différents en apparence, mais totalement indiscernables dans la nature. Ils peuvent être interprétés de telle manière que les drapeaux ont été sélectionnés et enregistrés dans le protocole dans un ordre différent, mais les «avions» eux-mêmes ont volé comme ils le devraient, c.-à-d. simultanément et simultanément jeté leurs drapeaux. Et dans quel ordre ils ont été enregistrés - le cas, comme on dit, est le dixième. L'essentiel est réalisé et rempli: la séquence et les heures de leur sortie correspondent au programme de vol



Le code peut être raccourci. Donc, apparemment, vous pouvez vous limiter au code d'une seule classe. Vous pouvez également masquer le code de la boucle d'événement. Si, en même temps, dans le code source, vous ouvrez le code du compartiment moteur, qui est caché derrière les opérateurs asyncio et attendez, alors la quantité de code d'automate ne sera probablement pas si effrayante.



3. Sur les problèmes d'implémentation d'automates en Python



Arrêtons-nous plus en détail sur les problèmes qui ont donné lieu à l'apparition du code automatique. La dernière chose à cacher semble jusqu'ici monstrueuse en comparaison avec le code source. Mais notons que le premier avion de Mozhaisky était loin d'être similaire au "séchage" moderne, et les premières voitures ne différaient pour le mieux d'aucun de leurs homologues modernes. Permettez-moi de souligner que les problèmes du code d'automate présenté sont largement liés à ma compréhension actuelle du langage Python et, peut-être, dans une moindre mesure, aux capacités du langage lui-même.



Néanmoins, le premier problème est lié au langage de description du modèle d'automate. En C ++, il est résolu au moyen du langage. Je ne vois pas de telles possibilités en Python. Malheureusement, comme on dit parfois maintenant, du mot du tout. Par conséquent, la méthode d'implémentation d'automates basée sur des opérateurs de contrôle du langage if-elif-else a été prise comme base. De plus, nous rappelons que dans le CPSU (a), en plus des automates eux-mêmes, la mémoire d'ombre et les espaces d'automates ont été introduits pour implémenter pleinement le parallélisme. Sans cela, les possibilités de programmation d'automates sont très limitées et à bien des égards inférieures.



Le problème suivant que nous avons déjà mentionné est celui des pointeurs. Il n'y a aucun problème avec eux en C ++. Dans le cadre du CPSU (a), conformément au paradigme de la POO, une classe d'automates de base a été créée, à partir de laquelle des classes d'automates appliquées sont générées, et leurs paramètres peuvent être non seulement des pointeurs, mais même leurs adresses. Tout cela permet de décrire et de mettre en œuvre de manière simple, compacte et très efficace toute tâche comprenant de nombreux processus interactifs parallèles.



Voici le code des classes d'automates C ++ équivalent à l'exemple considéré. Le code de retard du Listing 4 est équivalent à la ligne await asyncio.sleep (1) du Listing 1. Sous forme graphique, il correspond au modèle d'automate FAwaitSleep de la Figure 1. 1. Tel et seul un tel automate peut être considéré comme asynchrone et il ne ralentira pas le flux de calcul. FSleep dans la même figure correspond à l'opérateur habituel sleep (). Il est plus simple, mais garanti de détruire le modèle de temps discret en raison de l'action de y1 provoquant le retard séquentiel habituel. Et ce n'est plus bon à rien.



Listing 4. Code de délai asynchrone
//  (  )
#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; }




Le code C ++ pour le spider-plane est montré dans le Listing 5. Ce code est beaucoup plus adapté à son modèle que le diagramme du code Python. Surtout si l'on compare la table de transition de l'automate et l'apparence du graphe de l'automate. Ce sont simplement des formes différentes de décrire le même concept abstrait - un automate. Il montre également comment un pointeur vers la classe parente est passé lors de la création d'un délai (voir l'appel à la méthode FCall dans l'activité y1)



Listing 5. Code pour un avion d'araignée qui simule la lecture des pages du site
// "".   
#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; }




Il n'y a pas de code qui implémente les fonctions de la soi-disant boucle d'événements. Il n'y en a tout simplement pas besoin. ses fonctions sont remplies par le cœur de l'environnement CPSU (a). Il crée des objets et gère leur exécution parallèle en temps discret.



4. Conclusions



La brièveté n'est pas toujours la sœur du talent, et parfois c'est aussi un signe de langue. Certes, il est difficile de distinguer immédiatement l'un de l'autre. Le code Python sera souvent plus court que le code C ++. Mais c'est typique pour les cas simples. Plus la solution est complexe, moins cette différence sera. En fin de compte, même la complexité de la solution est déterminée par les capacités du modèle. Un modèle d'automate est beaucoup plus puissant qu'un schéma de principe.



Les automates et la parallélisation sont, avant tout, des moyens très efficaces pour résoudre des problèmes de complexité, la combattre, et pas tant un moyen d'augmenter la vitesse d'un programme. Comme tout cela est un modèle d’automate, le parallélisme est difficile à implémenter en Python, puis, malgré toutes ses puces, ses batteries et bien plus encore, il est difficile de me persuader dans sa direction. Je prêterais plus d'attention à l'environnement C ++, et à l'introduction peu justifiée des mêmes coroutines. Ce modèle est temporaire et la raison de sa mise en œuvre est largement forcée. Et que ferons-nous de cette "béquille" lorsque le problème du choix d'un modèle parallèle sera résolu?



Par conséquent, désolé, mais ma préférence est toujours du côté C ++. Et si vous considérez le domaine de mes intérêts professionnels - les systèmes industriels du temps réel dur soi-disant "effrayant", alors je n'ai pas le choix en tant que tel. Oui, une sorte d'environnement, une sorte de service peut être créée en utilisant Python. C'est pratique, c'est rapide, il existe de nombreux prototypes, etc. etc. Mais le cœur de la solution, son modèle parallèle, la logique des processus eux-mêmes sont sans ambiguïté C ++, sans ambiguïté des automates. Ici, les automates, bien sûr, sont plus importants et dominent. Mais pas des coroutines :)



En plus ... Regardez la vidéo [2] , en prêtant attention à la mise en œuvre du modèle de fusée. À propos d'elle, à partir de la 12e minute environ, la vidéo raconte. Respect au conférencier pour l'utilisation des machines :) Et pour une friandise, une autre solution de [3]... C'est dans l'esprit de la programmation asynchrone et asyncio. En fait, tout a commencé avec cet exemple - l'implémentation d'automates imbriqués en Python. Ici, la profondeur d'imbrication est encore plus grande que dans l'exemple détaillé ci-dessus. Le listing 6 montre le code source et son homologue d'automate Python. En figue. 2 est le modèle de consommation automatique de thé, et le Listing 7 montre l'implémentation C ++ équivalente pour VKP (a). Comparer, analyser, tirer des conclusions, critiquer ...



Listing 6. Lire et boire du thé de manière asynchrone en 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




Figure: 2. Modèle automatique de consommation de thé
image



Listing 7. Lire et boire du thé de manière asynchrone en 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



Déjà après avoir écrit l'article, après avoir lu la traduction de l' article de Yerain Diaz [4] , je me suis familiarisé avec un autre regard assez intéressant et plutôt admiratif sur les coroutines en général et asyncio en particulier. Malgré ce fait et d'autres comme lui, nous allons toujours "aller dans l'autre sens" :) Je suis d'accord sur une seule chose avec Rob Pike, que "la concurrence n'est pas parallèle". La compétitivité, on pourrait même dire plus dure, n'a rien à voir avec le parallélisme. Et il est à noter que Google Translate traduit cette phrase par "Le parallélisme n'est pas le parallélisme". L'homme nommé Google a certainement tort. Mais quelqu'un l'a convaincu de cela? :)



Littérature



  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 pour le développeur Python pratiquant. [Ressource électronique], mode d'accès: www.youtube.com/watch?v=PaY-hiuE5iE , gratuit. Langue. russe (date du traitement 08/01/2020).



All Articles