7. cvičenie – Asynchrónne programovanie v Pythone pomocou koprogramov (cez rozšírené generátory)

Príklad z prednášky

Podľa prednášky implementujte a v krátkom programe použite:

  1. jednoduchý generátor; vami implementovaný generátor vyskúšajte v cykle aj mimo cyklu;
  2. koprogram pomocou rozšíreného generátora.

Pri použití koprogramu nezabudnite na „inicializáciu“ pomocou volania funkcie next() (alebo metódy send(None) nad vytvoreným objektom rozšíreného generátora). Ak ste sa oboznámili s dekorátormi v Pythone, spokojne môžete implementovať aj dekorátor, ktorý vám uľahčí vytváranie koprogramov. Dekorátor by mal:

  1. inštanciovať koprogram, ktorý dekoruje,
  2. vyvolať funkciu next() s argumentom vytvoreného objektu koprogramu,
  3. a na záver vrátiť objekt koprogramu.

Príklad asynchrónneho programu

Hľadanie počtu výskytov jedného slova

Napíšte program, ktorý v nejakom textovom súbore (súbor môže byť napevno daný v programe) nájde počet výskytov nejakého slova (slovo môže byť napevno dané v programe). Tento program nech je implementovaný pomocou koprogramov rozšírených generátorov.

Pri riešení môžeme rozdeliť problém do troch častí (aby sme pre edukatívne účely využili čo najviac koprogramov 😉):

  1. čítanie súboru riadok po riadku,
  2. počítanie počtu výskytov vstupného slova v riadku,
  3. pripočítavanie zisteného počtu výskytov slova v riadku k celkovému výsledku.

Ešte by bolo dobré napísať kód, ktorý tieto funkcie „oživí“. Príklad jednoduchého použitia môže vyzerať nasledovne:

Je snáď dostatočne zrejmé, že vo vašich programoch je potrebné zmeniť obsahy premenných ‚f‘ a ‚substring‘ tak, aby ste dosiahli želané výsledky.

Hľadanie počtu výskytov viacerých slov

V doteraz vytvorenom kóde nie je veľmi vidno víťazstvo asynchrónneho prístupu. Máme jednu funkciu cat(), ktorá načítaný vstup odovzdá jedinej čakajúcej funkcii grep(), ktorá zas posiela výsledok svojho spracovanie jedinej čakajúcej funkcii wc(). Aby to bolo zaujímavejšie, skúsme tento jednoduchý program rozšíriť o možnosť spracovania (vyhľadania počtu výskytov) viacerých slov!

Pre každé slovo, ktorého výskyt v súbore nás zaujíma, vytvoríme práve jednu dvojicu inštancií funkcií grep() a wc(). Potom vložíme akúsi ‚medzivrstvu‘ za funkciu cat(); úlohou tejto medzivrstvy bude doručiť každej čakajúcej funkcii grep() riadok textu, ktorý medzivrstva obdrží od funkcie cat().

V nasledovnom kóde predpokladáme, že implementácia funkcií cat(), grep() a wc() sa nezmenila!!!

Dosiahli sme úžasnú vec: bezo zmeny funkcií grep(), wc() a cat() sme pridaním ďalšieho koprogramu rozšírili program o možnosť hľadania počtu výskytov z jedného na takmer ľubovoľný počet slov.

Záver

Na záver tohto bloku dodávame, že Python poskytuje možnosť využívať ‚pravé‘ koprogramy pomocou async def a await až od verzie 3.5. Pre jednoduchosť sme sa však rozhodli načrtnúť možnosti asynchrónneho programovania pomocou rozšírených generátorov, prítomných v jazyku Python od verzie 2.5.

Asynchrónne programovanie je mocný nástroj, avšak ako všetky nástroje, aj tento treba používať s rozvahou a iba tam, kde je to vhodné: v aplikáciách, ktoré veľmi veľa času strávia pri čakaní na vstupno-výstupné operácie. Pri vývoji asynchrónnych programov je však potrebné počítať s vyššou zložitosťou programového kódu. Takmer nič však nie je vo svete zadarmo; v tomto prípade platí, že zvýšenie výkonu získame za cenu vyššej zložitosti kódu.

Pri písaní asynchrónnych funkcií je potrebné dávať veľký pozor na to, aké funkcie v tele koprogramu používame. Treba sa vyhýbať takým, ktoré vedú k blokovaniu, pretože tým sa stráca zmysel asynchrónneho kódu. Plne asynchrónna aplikácia si vyžaduje napísanie vlastného plánovača na vyvolávanie behu koprogramov, pretože operačný systém nedokáže plánovať na beh koprogramy aplikácie tak, ako je to v prípade procesov a/alebo vlákien. Takýto plánovač pridáva ovšem ďalšiu zložitosť kódu. Čítanie programového kódu asynchrónnej aplikácie je poriadnou výzvou, ako ste si mali možnosť všimnúť už na triviálnom programčeku tohto cvičenia.