Ve cvičení použijeme ukázku z PyQt5. Máte-li ještě virtualenv s nainstalovaným PyQt, použijte ho, případně ho podle lekce o PyQt nainstalujte znovu.

K PyQt si přiinstalujte knihovnu quamash:

$ python -m pip install quamash

Nejde-li to, nevadí – nezbytné dnes PyQt nebude.

Navíc si nainstalujte knihovnu aiohttp:

$ python -m pip install aiohttp

AsyncIO

Pojďme si povídat o souběžnosti – možnostech, jak nechat počítač dělat víc úloh věcí najednou.

Jak jsme si řekli v lekci o C API, Python má globální zámek, takže pythonní kód může běžet jen v jednom vlákně najednou. Taky jsme si řekli, že to většinou příliš nevadí: typický síťový nebo GUI program stráví hodně času čekáním na události (odpověď z internetu, kliknutí myší atp.) a u tohoto čekání není potřeba držet zámek zamčený.

Servery typicky při zpracovávání požadavku stráví většinu času síťovou komunikací. Proto se často spouští několik vláken nebo přímo procesů najednou, aby se mohl vytížit procesor. Při velkém množství vláken ale nastanou dva problémy. První je, že vláken nemůže být neomezeně mnoho. Každé vlákno potřebuje vlastní stack, tj. poměrně velkou část paměti; a počet vláken bývá omezen i jinak (na Linuxu je globální limit počtu procesů, do kterého se počítají i jednotlivá vlákna – viz cat /proc/sys/kernel/threads-max). Druhý problém je, že přepnutí z jednoho vlákna do druhého se může stát kdykoli. Ověřit si, že je na to program připravený, je poměrně složité a na zajištění správné funkčnosti je potřeba zamykání či jiné techniky. Ty bývají relativně pomalé, a tak se jim programátoři snaží vyhnout. A chyby vzniklé nesprávným ošetřením přepínání vláken bývají složité na odhalení a vyřešení.

Vlákna jsou příklad preemptivního multitaskingu, kdy operační systém rozhoduje, kdy přepne z jednoho vlákna do druhého, a tuto změnu si prakticky vynutí. Jednotlivá vlákna se s tím musí vyrovnat. Alternativou je kooperativní multitasking, kdy se jednotlivé úlohy umí samy vzdát procesorového času, když např. čekají na síťovou komunikaci. Programátor tak ví, že dokud takto nepředá kontrolu ostatním úlohám, žádná jiná úloha mu pod rukama nemůže měnit stav procesu. Na druhou stranu je ale potřeba dostatečně často kontrolu předávat, aby se všechny úlohy dostaly ke slovu. Tuto techniku tak nemůže používat operační systém, pod kterým můžou běžet i špatně napsané programy. V rámci jednoho procesu se to ale dá s úspěchem využít.

Souběžnost v Pythonu

V Pythonu existovala a existuje řada knihoven, které nám umožňují „dělat více věcí zároveň“. Pro preemptivní multitasking jsou tu threading, tedy podpora pro vlákna, a multiprocessing, tedy způsob jak spustit nový pythonní proces, ve kterém se provede určitá funkce (přičemž vstup a výstup se předává serializovaný přes pipes).

Další knihovna, kterou lze z PyPI nainstalovat, je greenlet. Ta nám dává k dispozici tzv. mikro-vlákna, která se mezi sebou přepínají v rámci jednoho procesu. Na rozdíl od systémových vláken nepotřebují tolik paměti navíc, ale stále jde (alespoň z pohledu programátora) o preemptivní strategii: k přepnutí může dojít kdykoli, je tedy potřeba zamykat a složitě hledat málo časté chyby.

Byly vyvinuty i knihovny pro kooperativní přepínání, založené na tzv. futures (které vysvětlíme vzápětí). Nejznámější jsou Twisted a Tornado. Obě jsou relativně staré (2002, resp. 2009), ale stále populární.

Ačkoli byly Twisted, Tornado a podobné knihovny užitečné, jejich problém byl v tom, že má každá jiné API. Vznikaly tak kolem nich ekosystémy vázané na konkrétní knihovnu: server napsaný pro Tornado se nedal použít pod Twisted a aplikace využívající Twisted nemohla využít knihovnu pro Tornado.

Jak to vyřešit?

Jeden standard

xkcd 927

Komiks xkcd, © Randall Munroe, CC-BY-NC

Podobně jako přístup k různým SQL databázím je v Pythonu standardizovaný (knihovny pro SQLite, Postgres, MySQL atd. všechny podporují API definované v PEP 249) nebo je standardizované API webových serverů (WSGI, PEP 3333), tak vzniklo standardizované API pro kooperativní multitasking. Toto API je definováno v PEP 3156 a jeho referenční implementace, asyncio, je od Pythonu 3.4 ve standardní knihovně. (Pro Python 3.3 se dá asyncio nainstalovat pomocí pip.) Interně je asyncio postavené na konceptu futures inspirovaných Tornado/Twisted, ale jeho „hlavní“ API je postavené na coroutines podobných generátorům.

Od Pythonu verze 3.5 používá asyncio místo „normálních“ generátorů speciální syntaxi, která umožňuje kombinovat asynchronní funkce s příkazy for a with nebo i yield. Tuto syntaxi použijeme i tady; máte-li starší Python, podívejte se na potřebné změny uvedené níže.

Jak vypadá taková asynchronní funkce? Definuje se pomocí async def místo def, a může používat příkaz await.

Ukažme si to na příkladu:

import asyncio

async def count(name, interval):
    """Prints numbers from 0 in regular intervals"""
    i = 0
    while True:
        print(name, 'counts', i)
        await asyncio.sleep(interval)
        i += 1


loop = asyncio.get_event_loop()
asyncio.ensure_future(count('Quick', 0.3))
asyncio.ensure_future(count('Slow', 1))
loop.run_forever()
loop.close()

Co se tu děje? Příkazem await asyncio.sleep(interval) se asynchronní funkce zastaví (podobně jako generátor při yield) a předá kontrolu knihovně asyncio s informací že za daný čas by kontrolu chtěla zase zpátky. Než daný interval uplyne, asyncio může spouštět jiné úlohy; po jeho uplynutí naši čekající funkci „probudí“.

Spouštění a ukončení se dělá poněkud krkolomě. Pojďme se podívat co všechno se skrývá v posledních pěti příkazech.

V Pythonu verze 3.4 a nižší ještě neexistovala klíčová slova async a await; asynchronní funkce byly opravdu implementovány jako generátory. Máte-li starší verzi Pythonu, je potřeba místo:

async def ...:
    await ...

psát:

@asyncio.coroutine
def ...:
    yield from ...

Starý způsob zatím funguje i v novějším Pythonu, a dokonce se někdy objevuje i v dokumentaci.

Event Loop

Knihovna asyncio nám dává k dispozici smyčku událostí, která se, podobně jako app.exec v Qt, stará o plánování jednotlivých úloh. Každé vlákno může mít vlastní smyčku událostí, kterou získáme pomocí asyncio.get_event_loop a pak ji můžeme spustit dvěma způsoby:

  • loop.run_forever() spustí smyčku na tak dlouho, dokud jsou nějaké úlohy naplánovány (to trochu odporuje názvu, ale většinou se nestává, že by se úlohy „vyčerpaly“), nebo
  • loop.run_until_complete(task) – tahle funkce skončí hned, jakmile je hotová daná úloha, a vrátí její výsledek.
  • Od Pythonu 3.7 můžete použít jednoduché asyncio.run(task), aniž byste museli explicitně pracovat s určitou smyčkou událostí. Jedná se ale o API, které se v budoucnu může změnit.

Futures

Jak už bylo řečeno, knihovna asyncio je uvnitř založená na futures. Copak to je?

Future je objekt, který reprezentuje budoucí výsledek nějaké operace. Poté, co tato operace skončí, se výsledek dá zjistit pomocí metody result(); jestli je operace hotová se dá zjistit pomocí done(). Future je taková „krabička“ na vrácenou hodnotu – než tam něco tu hodnotu dá, musíme počkat; poté je hodnota stále k dispozici. Tohle čekání se dělá pomocí await (nebo loop.run_until_complete).

import asyncio


async def set_future(fut):
    """Sets the value of a Future, after a delay"""
    print('set_future: sleeping...')
    await asyncio.sleep(1)
    print('set_future: setting future')
    fut.set_result(123)
    print('set_future done.')


async def get_future(fut):
    """Receives the value of a Future, once it's ready"""
    print('get_future: waiting for future...')
    await fut
    print('get_future: getting result')
    result = fut.result()
    print('get_future: done')
    return result


future = asyncio.Future()


# Schedule the "set_future" task (explained later)
asyncio.ensure_future(set_future(future))


# Run the "get_future" coroutine until complete
loop = asyncio.get_event_loop()
result = loop.run_until_complete(get_future(future))
loop.close()

print('Result is', result)

Do Future se dá vložit i výjimka. To se využívá v případě, že úloha, která má Future naplnit, selže. Metoda result() potom tuto výjimku způsobí v kódu, který by výsledek zpracovával.

Na Future se navíc dají navázat funkce, které se zavolají, jakmile je výsledek k dispozici. Dá se tak implementovat callback styl programování (který možná znáte např. z Node.js). Pomocí futures & callbacks se před nástupem generátorů programovalo pro knihovny jako Twisted.

Podobně jako yield se await dá použít jako výraz, jehož hodnota je výsledek dané Future. Funkci get_future z příkladu výše tak lze napsat stručněji:

async def get_future(fut):
    """Receives the value of a Future, once it's ready"""
    return (await fut)

Další vlastnost Future je ta, že se dá „zrušit“: pomocí Future.cancel() signalizujeme úloze, která má připravit výsledek, že už ten výsledek nepotřebujeme. Po zrušení bude result() způsobovat CancelledError.

Async funkce a Task

Používání Future (nebo callback funkcí) je poněkud těžkopádné. V asyncio se Future používají hlavně proto, že je na ně jednoduché navázat existující knihovny. Aplikační kód je ale lepší psát pomocí asynchronních funkcí, tak jako v příkladu výše.

Asynchronní funkce se dají kombinovat pomocí await podobně jako generátory pomocí yield from. Nevýhoda asynchronních funkcí spočívá v tom, že na každé zavolání takové funkce lze použít jen jeden await. Na rozdíl od Future se výsledek nikam neukládá; jen se po skončení jednou předá.

import asyncio

async def add(a, b):
    await asyncio.sleep(1)
    return a + b

async def demo():
    coroutine = add(2, 3)
    print('The result is:', (await coroutine))
    print('The result is:', (await coroutine))  # chyba!


loop = asyncio.get_event_loop()
result = loop.run_until_complete(demo())
loop.close()

Tenhle problém můžeme vyřešit tak, že asynchronní funkci „zabalíme“ do Future. Na to ma dokonce asyncio speciální funkci ensure_future, která:

  • dostane-li asynchronní funkci, „zabalí“ ji do Future, a
  • výsledek přímo naplánuje na smyčce událostí, takže se asynchronní funkce časem začne provádět.
async def demo():
    coroutine = asyncio.ensure_future(add(2, 3))
    print('The result is:', (await coroutine))
    print('The result is:', (await coroutine))  # OK!

Výsledek ensure_future je speciální druh Future zvaný Task. Ten má několik vlastností navíc, ale v podstatě ho zmiňujieme jen proto, abyste věděli co Task znamená, až se vám objeví v chybové hlášce.

Fan-Out a Fan-In

S pomocí asynchronních funkcí můžeme nad našimi programy přemýšlet tak, jako by to byly „normální“ procedurálně zapsané algoritmy: máme jedno „vlákno“, které se provádí od začátku do konce, jen na některých místech (označených await) se provádění přeruší a zatímco náš kód čeká na výsledek nějaké operace, může se spustit jiný kus kódu. Funkce, na které je takto potřeba čekat, bývají v dokumentaci patřičně označeny (v síťovém programování je to většinou čtení ze socketů nebo inicializace či ukončení serveru).

Pomocí ensure_future a await můžeme k tomu dělat něco navíc: rozdělit běh našeho programu na víc úloh, které se budou vykonávat „souběžně“ – například autor scraperu chce stáhnout několik stránek najednou nebo server souběžně odpovídá na několik požadavků. Tomuto rozdělení se říká fan-out.

Opačná operace je fan-in, kdy několik úloh opět spojíme do jedné. Výše uvedený scraper může počkat, než jsou všechny stránky stažené – třeba pomocí jednoho await pro každý Task, po kterém může pokračovat zpracováním získaných dat.

Co se týče webového serveru, může se zdát, že tady není potřeba explicitně počkat na výsledek každého úkolu. Ale není to tak. I tady je poměrně důležité na každou úlohu nastartovanou pomocí ensure_future „počkat“ pomocí např. await – už jen proto, abychom zachytili případnou výjimku. Neuděláme-li to, asyncio bude vypisovat varovné hlášky.

Asynchronní cykly a kontexty

Až budete používat některé „asynchronní“ knihovny, setkáte se pravděpodobně se dvěma novými konstrukcemi: async for a async with.

Fungují jako jejich „ne-async“ varianty, jen na začátku a konci každé iterace (resp. na začátku a konci bloku) můžou přerušit vykonávání funkce – podobně jako await.

Typický příklad je u databází: začátek a konec transakce i získávání jednotlivých řádků pravděpodobně potřebují komunikaci po síti, takže hypotetická databázová knihovna by se mohla používat nějak takto:

async with database.transaction_context():
    await database.execute('UPDATE ...')
    async for row in (await database.execute('SELECT ...')):
        handle(row)

A další

Nakonec několik tipů, o kterých je dobré vědět.

V asyncio najdeme synchronizační mechanismy známé z vláknového programování, např. Lock a Semaphore – viz dokumentace.

Musíme-li použít blokující funkci, která např. komunikuje po síti bez await a která by tedy zablokovala i všechny ostatní úlohy, můžeme použít loop.run_in_executor(), a tím danou funkci zavolat ve vlákně nebo podprocesu, ale výsledek zpřístupnit pomocí asyncio.Future. Použití je opět popsáno v dokumentaci.

Občas vás při programování s asyncio zaskočí zrádná chyba. V takových případech je dobré zapnout debug režim pomocí proměnné prostředí PYTHONASYNCIODEBUG=1. V tomto režimu asyncio upozorňuje na časté chyby, do některých chybových výpisů přidává informaci o tom, kde aktuální Task vznikl, apod. Více informací je zase v dokumentaci.

Alternativní smyčky událostí

Jak bylo zmíněno na začátku, hlavní cíl asyncio je definovat společné rozhraní pro různé asynchronní knihovny, aby bylo možné např. kombinovat knihovny pro Tornado se smyčkou událostí v Twisted. Samotné asyncio je jen jedna z mnoha implementací tohoto rozhraní. Zajímavá je například knihovna uvloop, která je asi 2-4× rychlejší než asyncio (ale má závislosti, které se pro součást standardní knihovny nehodí).

Další zajímavá implementace je Quamash, která pod standardním asyncio API používá smyčku událostí z Qt. Umožňuje tak efektivně zpracovávat Qt události zároveň s asynchronními funkcemi známými z asyncio.

Event loop z quamash je potřeba na začátku programu naimportovat a nastavit jako hlavní smyčku událostí, a poté ji, místo Qt-ovského app.exec(), spustit. Jednotlivé asynchronní funkce se pak používají jako v čistém asyncio: pomocí asyncio.ensure_future, await, atd.

Ukázka:

import asyncio

from PyQt5 import QtGui, QtWidgets
from quamash import QEventLoop

app = QtWidgets.QApplication([])
loop = QEventLoop(app)
asyncio.set_event_loop(loop)

display = QtWidgets.QLCDNumber()
display.setWindowTitle('Stopwatch')

display.show()

async def update_time():
    value = 0
    while True:
        display.display(value)
        await asyncio.sleep(1)
        value += 1

asyncio.ensure_future(update_time())

loop.run_forever()

Komunikace

Ono io v asyncio naznačuje, že je tato knihovna dělaná především na vstup a výstup – konkrétně na komunikaci přes síť (případně s jinými procesy).

Ke komunikaci používá asyncio tři úrovně abstrakce: Transport, Protocol a Stream. V krátkosti si je tu popíšeme; detaily pak najdete v dokumentaci (je pro nás totiž mnohem důležitější abyste pochopili principy, než abyste uměli konkrétní API, které lze dohledat v dokumentaci).

Transporty a protokoly jsou postaveny na konceptech knihovny Twisted.

Transport zajišťuje samotné posílání bajtů mezi počítači (transportní vrstvu), kdežto Protocol implementuje nějaký aplikační protokol. Transport většinou nepíšeme sami, použijeme existující. V asyncio jsou zabudované transporty pro TCP, UDP a SSL. Protocol je pak použit pro implementaci konkrétních protokolů jako HTTP, FTP a podobně. V dokumentaci najdete podrobnější popis včetně příkladů.

Kromě toho existuje i „Stream API“ založené na asynchronních funkcích. Většinou platí, že operace otevření, čtení, flush a zavření Streamu jsou asynchronní funkce (v dokumentaci označované jako coroutines), a je tedy nutné je použít s await; oproti tomu zápis asynchronní není – data se uloží do bufferu a pošlou se, až to bude možné.

Typicky ale místo čistého asyncio použijeme existující knihovnu. Tady je příklad z knihovny aiohttp, která implementuje server a klienta pro HTTP:

import asyncio
import aiohttp

async def main(url):
    # Use a a session
    session = aiohttp.ClientSession()
    async with session:

        # Get the response (acts somewhat like a file; needs to be closed)
        async with session.get(url) as response:

            # Fetch the whole text
            html = await response.text()
            print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main('http://python.cz'))
loop.close()
{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/brno-jaro-knihovny:threads:1",
      "title": "AsyncIO",
      "html": "\n          \n    \n\n    <p>Ve cvi&#x10D;en&#xED; pou&#x17E;ijeme uk&#xE1;zku z PyQt5.\nM&#xE1;te-li je&#x161;t&#x11B; virtualenv s&#xA0;nainstalovan&#xFD;m PyQt, pou&#x17E;ijte ho, p&#x159;&#xED;padn&#x11B; ho\npodle <a href=\"/2019/brno-jaro-knihovny/intro/pyqt/\">lekce o PyQt</a> nainstalujte znovu.</p>\n<p>K PyQt si p&#x159;iinstalujte knihovnu <code>quamash</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">$ </span>python -m pip install quamash\n</pre></div><p>Nejde-li to, nevad&#xED; &#x2013; nezbytn&#xE9; dnes PyQt nebude.</p>\n<p>Nav&#xED;c si nainstalujte knihovnu <code>aiohttp</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">$ </span>python -m pip install aiohttp\n</pre></div><hr>\n<h1>AsyncIO</h1>\n<p>Poj&#x10F;me si pov&#xED;dat o soub&#x11B;&#x17E;nosti &#x2013; mo&#x17E;nostech, jak nechat po&#x10D;&#xED;ta&#x10D; d&#x11B;lat v&#xED;c\n&#xFA;loh v&#x11B;c&#xED; najednou.</p>\n<p>Jak jsme si &#x159;ekli v&#xA0;<a href=\"/2019/brno-jaro-knihovny/intro/cython/\">lekci o C API</a>, Python m&#xE1; glob&#xE1;ln&#xED; z&#xE1;mek, tak&#x17E;e pythonn&#xED; k&#xF3;d\nm&#x16F;&#x17E;e b&#x11B;&#x17E;et jen v jednom vl&#xE1;kn&#x11B; najednou.\nTaky jsme si &#x159;ekli, &#x17E;e to v&#x11B;t&#x161;inou p&#x159;&#xED;li&#x161; nevad&#xED;: typick&#xFD; s&#xED;&#x165;ov&#xFD; nebo GUI program\nstr&#xE1;v&#xED; hodn&#x11B; &#x10D;asu &#x10D;ek&#xE1;n&#xED;m na ud&#xE1;losti (odpov&#x11B;&#x10F; z internetu, kliknut&#xED; my&#x161;&#xED; atp.)\na u tohoto &#x10D;ek&#xE1;n&#xED; nen&#xED; pot&#x159;eba dr&#x17E;et z&#xE1;mek zam&#x10D;en&#xFD;.</p>\n<p>Servery typicky p&#x159;i zpracov&#xE1;v&#xE1;n&#xED; po&#x17E;adavku str&#xE1;v&#xED; <em>v&#x11B;t&#x161;inu</em> &#x10D;asu s&#xED;&#x165;ovou komunikac&#xED;.\nProto se &#x10D;asto spou&#x161;t&#xED; n&#x11B;kolik vl&#xE1;ken nebo p&#x159;&#xED;mo proces&#x16F; najednou, aby se mohl vyt&#xED;&#x17E;it\nprocesor.\nP&#x159;i velk&#xE9;m mno&#x17E;stv&#xED; vl&#xE1;ken ale nastanou dva probl&#xE9;my.\nPrvn&#xED; je, &#x17E;e vl&#xE1;ken nem&#x16F;&#x17E;e b&#xFD;t neomezen&#x11B; mnoho.\nKa&#x17E;d&#xE9; vl&#xE1;kno pot&#x159;ebuje vlastn&#xED; <em>stack</em>, tj. pom&#x11B;rn&#x11B; velkou &#x10D;&#xE1;st pam&#x11B;ti; a po&#x10D;et vl&#xE1;ken\nb&#xFD;v&#xE1; omezen i jinak (na Linuxu je glob&#xE1;ln&#xED; limit po&#x10D;tu proces&#x16F;, do kter&#xE9;ho se po&#x10D;&#xED;taj&#xED;\ni jednotliv&#xE1; vl&#xE1;kna &#x2013; viz <code>cat /proc/sys/kernel/threads-max</code>).\nDruh&#xFD; probl&#xE9;m je, &#x17E;e p&#x159;epnut&#xED; z jednoho vl&#xE1;kna do druh&#xE9;ho se m&#x16F;&#x17E;e st&#xE1;t <em>kdykoli</em>.\nOv&#x11B;&#x159;it si, &#x17E;e je na to program p&#x159;ipraven&#xFD;, je pom&#x11B;rn&#x11B; slo&#x17E;it&#xE9; a na zaji&#x161;t&#x11B;n&#xED;\nspr&#xE1;vn&#xE9; funk&#x10D;nosti je pot&#x159;eba zamyk&#xE1;n&#xED; &#x10D;i jin&#xE9; techniky. Ty b&#xFD;vaj&#xED; relativn&#x11B;\npomal&#xE9;, a tak se jim program&#xE1;to&#x159;i sna&#x17E;&#xED; vyhnout.\nA chyby vznikl&#xE9; nespr&#xE1;vn&#xFD;m o&#x161;et&#x159;en&#xED;m p&#x159;ep&#xED;n&#xE1;n&#xED; vl&#xE1;ken b&#xFD;vaj&#xED; slo&#x17E;it&#xE9; na odhalen&#xED;\na vy&#x159;e&#x161;en&#xED;.</p>\n<p>Vl&#xE1;kna jsou p&#x159;&#xED;klad <em>preemptivn&#xED;ho multitaskingu</em>, kdy opera&#x10D;n&#xED; syst&#xE9;m rozhoduje,\nkdy p&#x159;epne z jednoho vl&#xE1;kna do druh&#xE9;ho, a tuto zm&#x11B;nu si prakticky vynut&#xED;.\nJednotliv&#xE1; vl&#xE1;kna se s t&#xED;m mus&#xED; vyrovnat.\nAlternativou je <em>kooperativn&#xED; multitasking</em>, kdy se jednotliv&#xE9; &#xFA;lohy um&#xED; <em>samy</em> vzd&#xE1;t\nprocesorov&#xE9;ho &#x10D;asu, kdy&#x17E; nap&#x159;. &#x10D;ekaj&#xED; na s&#xED;&#x165;ovou komunikaci.\nProgram&#xE1;tor tak v&#xED;, &#x17E;e dokud takto nep&#x159;ed&#xE1; kontrolu ostatn&#xED;m &#xFA;loh&#xE1;m, &#x17E;&#xE1;dn&#xE1; jin&#xE1;\n&#xFA;loha mu pod rukama nem&#x16F;&#x17E;e m&#x11B;nit stav procesu.\nNa druhou stranu je ale pot&#x159;eba dostate&#x10D;n&#x11B; &#x10D;asto kontrolu p&#x159;ed&#xE1;vat, aby se v&#x161;echny\n&#xFA;lohy dostaly ke slovu.\nTuto techniku tak nem&#x16F;&#x17E;e pou&#x17E;&#xED;vat opera&#x10D;n&#xED; syst&#xE9;m, pod kter&#xFD;m m&#x16F;&#x17E;ou b&#x11B;&#x17E;et i &#x161;patn&#x11B;\nnapsan&#xE9; programy. V&#xA0;r&#xE1;mci jednoho procesu se to ale d&#xE1; s&#xA0;&#xFA;sp&#x11B;chem vyu&#x17E;&#xED;t.</p>\n<h2>Soub&#x11B;&#x17E;nost v Pythonu</h2>\n<p>V Pythonu existovala a existuje &#x159;ada knihoven, kter&#xE9; n&#xE1;m umo&#x17E;&#x148;uj&#xED; &#x201E;d&#x11B;lat v&#xED;ce\nv&#x11B;c&#xED; z&#xE1;rove&#x148;&#x201C;.\nPro preemptivn&#xED; multitasking jsou tu <code>threading</code>, tedy podpora pro vl&#xE1;kna,\na <code>multiprocessing</code>, tedy zp&#x16F;sob jak spustit nov&#xFD; pythonn&#xED; proces,\nve kter&#xE9;m se provede ur&#x10D;it&#xE1; funkce\n(p&#x159;i&#x10D;em&#x17E; vstup a v&#xFD;stup se p&#x159;ed&#xE1;v&#xE1; serializovan&#xFD; p&#x159;es <em>pipes</em>).</p>\n<p>Dal&#x161;&#xED; knihovna, kterou lze z PyPI nainstalovat, je <a href=\"https://greenlet.readthedocs.io/en/latest/\">greenlet</a>.\nTa n&#xE1;m d&#xE1;v&#xE1; k dispozici tzv. <em>mikro-vl&#xE1;kna</em>,\nkter&#xE1; se mezi sebou p&#x159;ep&#xED;naj&#xED; v r&#xE1;mci jednoho procesu.\nNa rozd&#xED;l od syst&#xE9;mov&#xFD;ch vl&#xE1;ken nepot&#x159;ebuj&#xED; tolik pam&#x11B;ti nav&#xED;c, ale\nst&#xE1;le jde (alespo&#x148; z pohledu program&#xE1;tora) o <em>preemptivn&#xED;</em> strategii:\nk p&#x159;epnut&#xED; m&#x16F;&#x17E;e doj&#xED;t kdykoli,\nje tedy pot&#x159;eba zamykat a slo&#x17E;it&#x11B; hledat m&#xE1;lo &#x10D;ast&#xE9; chyby.</p>\n<p>Byly vyvinuty i knihovny pro <em>kooperativn&#xED;</em> p&#x159;ep&#xED;n&#xE1;n&#xED;, zalo&#x17E;en&#xE9; na tzv.\n<em>futures</em> (kter&#xE9; vysv&#x11B;tl&#xED;me vz&#xE1;p&#x11B;t&#xED;).\nNejzn&#xE1;m&#x11B;j&#x161;&#xED; jsou <a href=\"https://twistedmatrix.com/trac/\">Twisted</a> a <a href=\"http://www.tornadoweb.org/en/stable/\">Tornado</a>.\nOb&#x11B; jsou relativn&#x11B; star&#xE9; (2002, resp. 2009), ale st&#xE1;le popul&#xE1;rn&#xED;.</p>\n<p>A&#x10D;koli byly Twisted, Tornado a podobn&#xE9; knihovny u&#x17E;ite&#x10D;n&#xE9;, jejich probl&#xE9;m\nbyl v tom, &#x17E;e m&#xE1; ka&#x17E;d&#xE1; jin&#xE9; API.\nVznikaly tak kolem nich ekosyst&#xE9;my v&#xE1;zan&#xE9; na konkr&#xE9;tn&#xED; knihovnu:\nserver napsan&#xFD; pro Tornado se nedal pou&#x17E;&#xED;t pod Twisted a aplikace\nvyu&#x17E;&#xED;vaj&#xED;c&#xED; Twisted nemohla vyu&#x17E;&#xED;t knihovnu pro Tornado.</p>\n<p>Jak to vy&#x159;e&#x161;it?</p>\n<h2>Jeden standard</h2>\n<p><img src=\"https://imgs.xkcd.com/comics/standards.png\" alt=\"xkcd 927\"></p>\n<p><em>Komiks <a href=\"https://xkcd.com/927/\">xkcd</a>, &#xA9; Randall Munroe, <a href=\"https://creativecommons.org/licenses/by-nc/2.5/\">CC-BY-NC</a></em></p>\n<p>Podobn&#x11B; jako p&#x159;&#xED;stup k r&#x16F;zn&#xFD;m SQL datab&#xE1;z&#xED;m je v Pythonu standardizovan&#xFD;\n(knihovny pro SQLite, Postgres, MySQL atd. v&#x161;echny podporuj&#xED; API definovan&#xE9;\nv <a href=\"https://www.python.org/dev/peps/pep-0249/\">PEP 249</a>) nebo je standardizovan&#xE9; API webov&#xFD;ch server&#x16F; (WSGI, <a href=\"https://www.python.org/dev/peps/pep-3333/\">PEP 3333</a>),\ntak vzniklo standardizovan&#xE9; API pro kooperativn&#xED; multitasking.\nToto API je definov&#xE1;no v <a href=\"https://www.python.org/dev/peps/pep-3156/\">PEP 3156</a> a jeho referen&#x10D;n&#xED; implementace, <code>asyncio</code>,\nje od Pythonu 3.4 ve standardn&#xED; knihovn&#x11B;.\n(Pro Python 3.3 se d&#xE1; asyncio nainstalovat <a href=\"https://pypi.org/project/asyncio/\">pomoc&#xED; <code>pip</code></a>.)\nIntern&#x11B; je <code>asyncio</code> postaven&#xE9; na konceptu <em>futures</em> inspirovan&#xFD;ch Tornado/Twisted,\nale jeho &#x201E;hlavn&#xED;&#x201C; API je postaven&#xE9; na <em>coroutines</em> podobn&#xFD;ch gener&#xE1;tor&#x16F;m.</p>\n<p>Od Pythonu verze 3.5 pou&#x17E;&#xED;v&#xE1; <code>asyncio</code> m&#xED;sto &#x201E;norm&#xE1;ln&#xED;ch&#x201C; gener&#xE1;tor&#x16F;\nspeci&#xE1;ln&#xED; syntaxi, kter&#xE1; umo&#x17E;&#x148;uje kombinovat asynchronn&#xED; funkce s&#xA0;p&#x159;&#xED;kazy\n<code>for</code> a <code>with</code> nebo i <code>yield</code>.\nTuto syntaxi pou&#x17E;ijeme i tady; m&#xE1;te-li star&#x161;&#xED; Python, pod&#xED;vejte se na pot&#x159;ebn&#xE9; zm&#x11B;ny uveden&#xE9; n&#xED;&#x17E;e.</p>\n<p>Jak vypad&#xE1; takov&#xE1; asynchronn&#xED; funkce?\nDefinuje se pomoc&#xED; <code>async def</code> m&#xED;sto <code>def</code>, a m&#x16F;&#x17E;e pou&#x17E;&#xED;vat p&#x159;&#xED;kaz <code>await</code>.</p>\n<p>Uka&#x17E;me si to na p&#x159;&#xED;kladu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">asyncio</span>\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">count</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"n\">interval</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Prints numbers from 0 in regular intervals&quot;&quot;&quot;</span>\n    <span class=\"n\">i</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">name</span><span class=\"p\">,</span> <span class=\"s1\">&apos;counts&apos;</span><span class=\"p\">,</span> <span class=\"n\">i</span><span class=\"p\">)</span>\n        <span class=\"n\">await</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"n\">interval</span><span class=\"p\">)</span>\n        <span class=\"n\">i</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n\n\n<span class=\"n\">loop</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">get_event_loop</span><span class=\"p\">()</span>\n<span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">ensure_future</span><span class=\"p\">(</span><span class=\"n\">count</span><span class=\"p\">(</span><span class=\"s1\">&apos;Quick&apos;</span><span class=\"p\">,</span> <span class=\"mf\">0.3</span><span class=\"p\">))</span>\n<span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">ensure_future</span><span class=\"p\">(</span><span class=\"n\">count</span><span class=\"p\">(</span><span class=\"s1\">&apos;Slow&apos;</span><span class=\"p\">,</span> <span class=\"mi\">1</span><span class=\"p\">))</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">run_forever</span><span class=\"p\">()</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n</pre></div><p>Co se tu d&#x11B;je?\nP&#x159;&#xED;kazem <code>await asyncio.sleep(interval)</code> se asynchronn&#xED; funkce zastav&#xED;\n(podobn&#x11B; jako gener&#xE1;tor p&#x159;i <code>yield</code>) a p&#x159;ed&#xE1; kontrolu knihovn&#x11B; <code>asyncio</code>\ns&#xA0;informac&#xED; &#x17E;e za dan&#xFD; &#x10D;as by kontrolu cht&#x11B;la zase zp&#xE1;tky.\nNe&#x17E; dan&#xFD; interval uplyne, <code>asyncio</code> m&#x16F;&#x17E;e spou&#x161;t&#x11B;t jin&#xE9; &#xFA;lohy;\npo jeho uplynut&#xED; na&#x161;i &#x10D;ekaj&#xED;c&#xED; funkci &#x201E;probud&#xED;&#x201C;.</p>\n<p>Spou&#x161;t&#x11B;n&#xED; a ukon&#x10D;en&#xED; se d&#x11B;l&#xE1; pon&#x11B;kud krkolom&#x11B;.\nPoj&#x10F;me se pod&#xED;vat co v&#x161;echno se skr&#xFD;v&#xE1; v&#xA0;posledn&#xED;ch p&#x11B;ti p&#x159;&#xED;kazech.</p>\n<div class=\"admonition note\"><p>V Pythonu verze 3.4 a ni&#x17E;&#x161;&#xED; je&#x161;t&#x11B; neexistovala kl&#xED;&#x10D;ov&#xE1; slova <code>async</code> a\n<code>await</code>; asynchronn&#xED; funkce byly opravdu implementov&#xE1;ny jako gener&#xE1;tory.\nM&#xE1;te-li star&#x161;&#xED; verzi Pythonu, je pot&#x159;eba m&#xED;sto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"o\">...</span><span class=\"p\">:</span>\n    <span class=\"n\">await</span> <span class=\"o\">...</span>\n</pre></div><p>ps&#xE1;t:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"nd\">@asyncio.coroutine</span>\n<span class=\"k\">def</span> <span class=\"o\">...</span><span class=\"p\">:</span>\n    <span class=\"k\">yield from</span> <span class=\"o\">...</span>\n</pre></div><p>Star&#xFD; zp&#x16F;sob zat&#xED;m funguje i v&#xA0;nov&#x11B;j&#x161;&#xED;m Pythonu, a dokonce se n&#x11B;kdy objevuje\ni v&#xA0;dokumentaci.</p>\n</div><h2>Event Loop</h2>\n<p>Knihovna <code>asyncio</code> n&#xE1;m d&#xE1;v&#xE1; k dispozici <em>smy&#x10D;ku ud&#xE1;lost&#xED;</em>, kter&#xE1; se, podobn&#x11B; jako\n<code>app.exec</code> v Qt, star&#xE1; o pl&#xE1;nov&#xE1;n&#xED; jednotliv&#xFD;ch &#xFA;loh.\nKa&#x17E;d&#xE9; vl&#xE1;kno m&#x16F;&#x17E;e m&#xED;t vlastn&#xED; smy&#x10D;ku ud&#xE1;lost&#xED;, kterou z&#xED;sk&#xE1;me pomoc&#xED;\n<code>asyncio.get_event_loop</code> a pak ji m&#x16F;&#x17E;eme spustit dv&#x11B;ma zp&#x16F;soby:</p>\n<ul>\n<li><code>loop.run_forever()</code> spust&#xED; smy&#x10D;ku na tak dlouho, dokud jsou n&#x11B;jak&#xE9; &#xFA;lohy\nnapl&#xE1;nov&#xE1;ny (to trochu odporuje n&#xE1;zvu, ale v&#x11B;t&#x161;inou se nest&#xE1;v&#xE1;, &#x17E;e by se\n&#xFA;lohy &#x201E;vy&#x10D;erpaly&#x201C;), nebo</li>\n<li><code>loop.run_until_complete(task)</code> &#x2013; tahle funkce skon&#x10D;&#xED; hned, jakmile je hotov&#xE1;\ndan&#xE1; &#xFA;loha, a vr&#xE1;t&#xED; jej&#xED; v&#xFD;sledek.</li>\n<li>Od Pythonu 3.7 m&#x16F;&#x17E;ete pou&#x17E;&#xED;t jednoduch&#xE9; <code>asyncio.run(task)</code>, ani&#x17E; byste museli\nexplicitn&#x11B; pracovat s ur&#x10D;itou smy&#x10D;kou ud&#xE1;lost&#xED;. Jedn&#xE1; se ale o API, kter&#xE9; se\nv budoucnu m&#x16F;&#x17E;e zm&#x11B;nit.</li>\n</ul>\n<h2>Futures</h2>\n<p>Jak u&#x17E; bylo &#x159;e&#x10D;eno, knihovna <code>asyncio</code> je uvnit&#x159; zalo&#x17E;en&#xE1; na <em>futures</em>.\nCopak to je?</p>\n<p><code>Future</code> je objekt, kter&#xFD; reprezentuje budouc&#xED; v&#xFD;sledek n&#x11B;jak&#xE9; operace.\nPot&#xE9;, co tato operace skon&#x10D;&#xED;, se v&#xFD;sledek d&#xE1; zjistit pomoc&#xED; metody <code>result()</code>;\njestli je operace hotov&#xE1; se d&#xE1; zjistit pomoc&#xED; <code>done()</code>.\n<code>Future</code> je takov&#xE1; &#x201E;krabi&#x10D;ka&#x201C; na vr&#xE1;cenou hodnotu &#x2013; ne&#x17E; tam n&#x11B;co\ntu hodnotu d&#xE1;, mus&#xED;me po&#x10D;kat; pot&#xE9; je hodnota st&#xE1;le k dispozici.\nTohle &#x10D;ek&#xE1;n&#xED; se d&#x11B;l&#xE1; pomoc&#xED; <code>await</code> (nebo <code>loop.run_until_complete</code>).</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">asyncio</span>\n\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">set_future</span><span class=\"p\">(</span><span class=\"n\">fut</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Sets the value of a Future, after a delay&quot;&quot;&quot;</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;set_future: sleeping...&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">await</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;set_future: setting future&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">fut</span><span class=\"o\">.</span><span class=\"n\">set_result</span><span class=\"p\">(</span><span class=\"mi\">123</span><span class=\"p\">)</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;set_future done.&apos;</span><span class=\"p\">)</span>\n\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">get_future</span><span class=\"p\">(</span><span class=\"n\">fut</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Receives the value of a Future, once it&apos;s ready&quot;&quot;&quot;</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;get_future: waiting for future...&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">await</span> <span class=\"n\">fut</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;get_future: getting result&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">fut</span><span class=\"o\">.</span><span class=\"n\">result</span><span class=\"p\">()</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;get_future: done&apos;</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">result</span>\n\n\n<span class=\"n\">future</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">Future</span><span class=\"p\">()</span>\n\n\n<span class=\"c1\"># Schedule the &quot;set_future&quot; task (explained later)</span>\n<span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">ensure_future</span><span class=\"p\">(</span><span class=\"n\">set_future</span><span class=\"p\">(</span><span class=\"n\">future</span><span class=\"p\">))</span>\n\n\n<span class=\"c1\"># Run the &quot;get_future&quot; coroutine until complete</span>\n<span class=\"n\">loop</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">get_event_loop</span><span class=\"p\">()</span>\n<span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">run_until_complete</span><span class=\"p\">(</span><span class=\"n\">get_future</span><span class=\"p\">(</span><span class=\"n\">future</span><span class=\"p\">))</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Result is&apos;</span><span class=\"p\">,</span> <span class=\"n\">result</span><span class=\"p\">)</span>\n</pre></div><p>Do <code>Future</code> se d&#xE1; vlo&#x17E;it i v&#xFD;jimka.\nTo se vyu&#x17E;&#xED;v&#xE1; v&#xA0;p&#x159;&#xED;pad&#x11B;, &#x17E;e &#xFA;loha, kter&#xE1; m&#xE1; <code>Future</code> naplnit, sel&#x17E;e. \nMetoda <code>result()</code> potom tuto v&#xFD;jimku zp&#x16F;sob&#xED; v&#xA0;k&#xF3;du, kter&#xFD; by v&#xFD;sledek\nzpracov&#xE1;val.</p>\n<p>Na <code>Future</code> se nav&#xED;c daj&#xED; nav&#xE1;zat funkce, kter&#xE9; se zavolaj&#xED;, jakmile je\nv&#xFD;sledek k dispozici.\nD&#xE1; se tak implementovat <em>callback</em> styl programov&#xE1;n&#xED; (kter&#xFD; mo&#x17E;n&#xE1; zn&#xE1;te\nnap&#x159;. z&#xA0;Node.js). Pomoc&#xED; <em>futures &amp; callbacks</em> se p&#x159;ed n&#xE1;stupem\ngener&#xE1;tor&#x16F; programovalo pro knihovny jako <code>Twisted</code>.</p>\n<p>Podobn&#x11B; jako <code>yield</code> se <code>await</code> d&#xE1; pou&#x17E;&#xED;t jako v&#xFD;raz, jeho&#x17E;\nhodnota je v&#xFD;sledek dan&#xE9; <code>Future</code>.\nFunkci <code>get_future</code> z&#xA0;p&#x159;&#xED;kladu v&#xFD;&#x161;e tak lze napsat stru&#x10D;n&#x11B;ji:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">get_future</span><span class=\"p\">(</span><span class=\"n\">fut</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Receives the value of a Future, once it&apos;s ready&quot;&quot;&quot;</span>\n    <span class=\"k\">return</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">fut</span><span class=\"p\">)</span>\n</pre></div><p>Dal&#x161;&#xED; vlastnost <code>Future</code> je ta, &#x17E;e se d&#xE1; &#x201E;zru&#x161;it&#x201C;: pomoc&#xED; <code>Future.cancel()</code>\nsignalizujeme &#xFA;loze, kter&#xE1; m&#xE1; p&#x159;ipravit v&#xFD;sledek, &#x17E;e u&#x17E; ten v&#xFD;sledek\nnepot&#x159;ebujeme.\nPo zru&#x161;en&#xED; bude <code>result()</code> zp&#x16F;sobovat <code>CancelledError</code>.</p>\n<h2>Async funkce a Task</h2>\n<p>Pou&#x17E;&#xED;v&#xE1;n&#xED; <code>Future</code> (nebo <em>callback</em> funkc&#xED;) je pon&#x11B;kud t&#x11B;&#x17E;kop&#xE1;dn&#xE9;.\nV <code>asyncio</code> se <code>Future</code> pou&#x17E;&#xED;vaj&#xED; hlavn&#x11B; proto, &#x17E;e je na n&#x11B; jednoduch&#xE9;\nnav&#xE1;zat existuj&#xED;c&#xED; knihovny.\nAplika&#x10D;n&#xED; k&#xF3;d je ale lep&#x161;&#xED; ps&#xE1;t pomoc&#xED; asynchronn&#xED;ch funkc&#xED;, tak jako\nv&#xA0;p&#x159;&#xED;kladu v&#xFD;&#x161;e.</p>\n<p>Asynchronn&#xED; funkce se daj&#xED; kombinovat pomoc&#xED; <code>await</code> podobn&#x11B; jako gener&#xE1;tory\npomoc&#xED; <code>yield from</code>.\nNev&#xFD;hoda asynchronn&#xED;ch funkc&#xED; spo&#x10D;&#xED;v&#xE1; v tom, &#x17E;e na ka&#x17E;d&#xE9; zavol&#xE1;n&#xED; takov&#xE9; funkce\nlze pou&#x17E;&#xED;t jen jeden <code>await</code>.\nNa rozd&#xED;l od <code>Future</code> se v&#xFD;sledek nikam neukl&#xE1;d&#xE1;;\njen se po skon&#x10D;en&#xED; jednou p&#x159;ed&#xE1;.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">asyncio</span>\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">add</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"n\">await</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">demo</span><span class=\"p\">():</span>\n    <span class=\"n\">coroutine</span> <span class=\"o\">=</span> <span class=\"n\">add</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">)</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;The result is:&apos;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">coroutine</span><span class=\"p\">))</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;The result is:&apos;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">coroutine</span><span class=\"p\">))</span>  <span class=\"c1\"># chyba!</span>\n\n\n<span class=\"n\">loop</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">get_event_loop</span><span class=\"p\">()</span>\n<span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">run_until_complete</span><span class=\"p\">(</span><span class=\"n\">demo</span><span class=\"p\">())</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n</pre></div><p>Tenhle probl&#xE9;m m&#x16F;&#x17E;eme vy&#x159;e&#x161;it tak, &#x17E;e asynchronn&#xED; funkci &#x201E;zabal&#xED;me&#x201C; do <code>Future</code>.\nNa to ma dokonce <code>asyncio</code> speci&#xE1;ln&#xED; funkci <code>ensure_future</code>, kter&#xE1;:</p>\n<ul>\n<li>dostane-li asynchronn&#xED; funkci, &#x201E;zabal&#xED;&#x201C; ji do <code>Future</code>, a</li>\n<li>v&#xFD;sledek p&#x159;&#xED;mo napl&#xE1;nuje na smy&#x10D;ce ud&#xE1;lost&#xED;, tak&#x17E;e se asynchronn&#xED; funkce\n&#x10D;asem za&#x10D;ne prov&#xE1;d&#x11B;t.</li>\n</ul>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">demo</span><span class=\"p\">():</span>\n    <span class=\"n\">coroutine</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">ensure_future</span><span class=\"p\">(</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"mi\">3</span><span class=\"p\">))</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;The result is:&apos;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">coroutine</span><span class=\"p\">))</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;The result is:&apos;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">coroutine</span><span class=\"p\">))</span>  <span class=\"c1\"># OK!</span>\n</pre></div><div class=\"admonition note\"><p>V&#xFD;sledek <code>ensure_future</code> je speci&#xE1;ln&#xED; druh <code>Future</code> zvan&#xFD; <code>Task</code>.\nTen m&#xE1; n&#x11B;kolik vlastnost&#xED; nav&#xED;c, ale v&#xA0;podstat&#x11B; ho zmi&#x148;ujieme jen proto,\nabyste v&#x11B;d&#x11B;li co <code>Task</code> znamen&#xE1;, a&#x17E; se v&#xE1;m objev&#xED; v&#xA0;chybov&#xE9; hl&#xE1;&#x161;ce.</p>\n</div><h2>Fan-Out a Fan-In</h2>\n<p>S pomoc&#xED; asynchronn&#xED;ch funkc&#xED; m&#x16F;&#x17E;eme nad na&#x161;imi programy p&#x159;em&#xFD;&#x161;let tak,\njako by to byly &#x201E;norm&#xE1;ln&#xED;&#x201C; procedur&#xE1;ln&#x11B; zapsan&#xE9; algoritmy: m&#xE1;me jedno\n&#x201E;vl&#xE1;kno&#x201C;, kter&#xE9; se prov&#xE1;d&#xED; od za&#x10D;&#xE1;tku do konce, jen na n&#x11B;kter&#xFD;ch m&#xED;stech\n(ozna&#x10D;en&#xFD;ch <code>await</code>) se prov&#xE1;d&#x11B;n&#xED; p&#x159;eru&#x161;&#xED; a zat&#xED;mco n&#xE1;&#x161; k&#xF3;d &#x10D;ek&#xE1; na v&#xFD;sledek\nn&#x11B;jak&#xE9; operace, m&#x16F;&#x17E;e se spustit jin&#xFD; kus k&#xF3;du.\nFunkce, na kter&#xE9; je takto pot&#x159;eba &#x10D;ekat, b&#xFD;vaj&#xED; v dokumentaci pat&#x159;i&#x10D;n&#x11B;\nozna&#x10D;eny (v&#xA0;s&#xED;&#x165;ov&#xE9;m programov&#xE1;n&#xED; je to v&#x11B;t&#x161;inou &#x10D;ten&#xED; ze socket&#x16F; nebo inicializace\n&#x10D;i ukon&#x10D;en&#xED; serveru).</p>\n<p>Pomoc&#xED; <code>ensure_future</code> a <code>await</code> m&#x16F;&#x17E;eme k&#xA0;tomu d&#x11B;lat n&#x11B;co nav&#xED;c:\nrozd&#x11B;lit b&#x11B;h na&#x161;eho programu na v&#xED;c &#xFA;loh, kter&#xE9; se budou vykon&#xE1;vat &#x201E;soub&#x11B;&#x17E;n&#x11B;&#x201C; &#x2013;\nnap&#x159;&#xED;klad autor scraperu chce st&#xE1;hnout n&#x11B;kolik str&#xE1;nek najednou\nnebo server soub&#x11B;&#x17E;n&#x11B; odpov&#xED;d&#xE1; na n&#x11B;kolik po&#x17E;adavk&#x16F;.\nTomuto rozd&#x11B;len&#xED; se &#x159;&#xED;k&#xE1; <em>fan-out</em>.</p>\n<p>Opa&#x10D;n&#xE1; operace je <em>fan-in</em>, kdy n&#x11B;kolik &#xFA;loh op&#x11B;t spoj&#xED;me do jedn&#xE9;.\nV&#xFD;&#x161;e uveden&#xFD; scraper m&#x16F;&#x17E;e po&#x10D;kat, ne&#x17E; jsou v&#x161;echny str&#xE1;nky sta&#x17E;en&#xE9; &#x2013;\nt&#x159;eba pomoc&#xED; jednoho <code>await</code> pro ka&#x17E;d&#xFD; <code>Task</code>, po kter&#xE9;m m&#x16F;&#x17E;e\npokra&#x10D;ovat zpracov&#xE1;n&#xED;m z&#xED;skan&#xFD;ch dat.</p>\n<p>Co se t&#xFD;&#x10D;e webov&#xE9;ho serveru, m&#x16F;&#x17E;e se zd&#xE1;t, &#x17E;e tady nen&#xED; pot&#x159;eba explicitn&#x11B;\npo&#x10D;kat na v&#xFD;sledek ka&#x17E;d&#xE9;ho &#xFA;kolu.\nAle nen&#xED; to tak. I tady je pom&#x11B;rn&#x11B; d&#x16F;le&#x17E;it&#xE9; na ka&#x17E;dou &#xFA;lohu nastartovanou\npomoc&#xED; <code>ensure_future</code> &#x201E;po&#x10D;kat&#x201C; pomoc&#xED; nap&#x159;. <code>await</code> &#x2013; u&#x17E; jen proto, abychom\nzachytili p&#x159;&#xED;padnou v&#xFD;jimku.\nNeud&#x11B;l&#xE1;me-li to, <code>asyncio</code> bude vypisovat varovn&#xE9; hl&#xE1;&#x161;ky.</p>\n<h2>Asynchronn&#xED; cykly a kontexty</h2>\n<p>A&#x17E; budete pou&#x17E;&#xED;vat n&#x11B;kter&#xE9; &#x201E;asynchronn&#xED;&#x201C; knihovny, setk&#xE1;te se pravd&#x11B;podobn&#x11B; se dv&#x11B;ma\nnov&#xFD;mi konstrukcemi: <code>async for</code> a <code>async with</code>.</p>\n<p>Funguj&#xED; jako jejich &#x201E;ne-<code>async</code>&#x201C; varianty, jen na za&#x10D;&#xE1;tku a konci ka&#x17E;d&#xE9; iterace (resp.\nna za&#x10D;&#xE1;tku a konci bloku) m&#x16F;&#x17E;ou p&#x159;eru&#x161;it vykon&#xE1;v&#xE1;n&#xED; funkce &#x2013; podobn&#x11B; jako <code>await</code>.</p>\n<p>Typick&#xFD; p&#x159;&#xED;klad je u datab&#xE1;z&#xED;: za&#x10D;&#xE1;tek a konec transakce i z&#xED;sk&#xE1;v&#xE1;n&#xED; jednotliv&#xFD;ch\n&#x159;&#xE1;dk&#x16F; pravd&#x11B;podobn&#x11B; pot&#x159;ebuj&#xED; komunikaci po s&#xED;ti, tak&#x17E;e hypotetick&#xE1; datab&#xE1;zov&#xE1;\nknihovna by se mohla pou&#x17E;&#xED;vat n&#x11B;jak takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">async</span> <span class=\"k\">with</span> <span class=\"n\">database</span><span class=\"o\">.</span><span class=\"n\">transaction_context</span><span class=\"p\">():</span>\n    <span class=\"n\">await</span> <span class=\"n\">database</span><span class=\"o\">.</span><span class=\"n\">execute</span><span class=\"p\">(</span><span class=\"s1\">&apos;UPDATE ...&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">async</span> <span class=\"k\">for</span> <span class=\"n\">row</span> <span class=\"ow\">in</span> <span class=\"p\">(</span><span class=\"n\">await</span> <span class=\"n\">database</span><span class=\"o\">.</span><span class=\"n\">execute</span><span class=\"p\">(</span><span class=\"s1\">&apos;SELECT ...&apos;</span><span class=\"p\">)):</span>\n        <span class=\"n\">handle</span><span class=\"p\">(</span><span class=\"n\">row</span><span class=\"p\">)</span>\n</pre></div><h2>A dal&#x161;&#xED;</h2>\n<p>Nakonec n&#x11B;kolik tip&#x16F;, o kter&#xFD;ch je dobr&#xE9; v&#x11B;d&#x11B;t.</p>\n<p>V <code>asyncio</code> najdeme synchroniza&#x10D;n&#xED; mechanismy zn&#xE1;m&#xE9; z vl&#xE1;knov&#xE9;ho programov&#xE1;n&#xED;, nap&#x159;.\n<code>Lock</code> a <code>Semaphore</code> &#x2013; viz <a href=\"https://docs.python.org/3/library/asyncio-sync.html\">dokumentace</a>.</p>\n<p>Mus&#xED;me-li pou&#x17E;&#xED;t blokuj&#xED;c&#xED; funkci, kter&#xE1; nap&#x159;. komunikuje po s&#xED;ti bez <code>await</code> a kter&#xE1; by\ntedy zablokovala i v&#x161;echny ostatn&#xED; &#xFA;lohy, m&#x16F;&#x17E;eme pou&#x17E;&#xED;t\n<code>loop.run_in_executor()</code>, a t&#xED;m danou funkci zavolat ve vl&#xE1;kn&#x11B; nebo podprocesu, ale v&#xFD;sledek zp&#x159;&#xED;stupnit\npomoc&#xED; <code>asyncio.Future</code>.\nPou&#x17E;it&#xED; je op&#x11B;t pops&#xE1;no v <a href=\"https://docs.python.org/3/library/asyncio-eventloop.html#executor\">dokumentaci</a>.</p>\n<p>Ob&#x10D;as v&#xE1;s p&#x159;i programov&#xE1;n&#xED; s <code>asyncio</code> zasko&#x10D;&#xED; zr&#xE1;dn&#xE1; chyba.\nV takov&#xFD;ch p&#x159;&#xED;padech je dobr&#xE9; zapnout <em>debug</em> re&#x17E;im pomoc&#xED; prom&#x11B;nn&#xE9; prost&#x159;ed&#xED; <code>PYTHONASYNCIODEBUG=1</code>.\nV tomto re&#x17E;imu asyncio upozor&#x148;uje na &#x10D;ast&#xE9; chyby, do n&#x11B;kter&#xFD;ch chybov&#xFD;ch v&#xFD;pis&#x16F; p&#x159;id&#xE1;v&#xE1; informaci o tom,\nkde aktu&#xE1;ln&#xED; <code>Task</code> vznikl, apod.\nV&#xED;ce informac&#xED; je zase v <a href=\"https://docs.python.org/3/library/asyncio-dev.html#asyncio-dev\">dokumentaci</a>.</p>\n<h2>Alternativn&#xED; smy&#x10D;ky ud&#xE1;lost&#xED;</h2>\n<p>Jak bylo zm&#xED;n&#x11B;no na za&#x10D;&#xE1;tku, hlavn&#xED; c&#xED;l <code>asyncio</code> je definovat spole&#x10D;n&#xE9; rozhran&#xED;\npro r&#x16F;zn&#xE9; asynchronn&#xED; knihovny, aby bylo mo&#x17E;n&#xE9; nap&#x159;. kombinovat knihovny pro\nTornado se smy&#x10D;kou ud&#xE1;lost&#xED; v Twisted.\nSamotn&#xE9; <code>asyncio</code> je jen jedna z mnoha implementac&#xED; tohoto rozhran&#xED;.\nZaj&#xED;mav&#xE1; je nap&#x159;&#xED;klad knihovna <a href=\"https://pypi.org/project/uvloop/\">uvloop</a>, kter&#xE1; je asi 2-4&#xD7; rychlej&#x161;&#xED; ne&#x17E; <code>asyncio</code>\n(ale m&#xE1; z&#xE1;vislosti, kter&#xE9; se pro sou&#x10D;&#xE1;st standardn&#xED; knihovny nehod&#xED;).</p>\n<p>Dal&#x161;&#xED; zaj&#xED;mav&#xE1; implementace je <a href=\"https://pypi.org/project/Quamash/\">Quamash</a>, kter&#xE1; pod standardn&#xED;m <code>asyncio</code> API pou&#x17E;&#xED;v&#xE1;\nsmy&#x10D;ku ud&#xE1;lost&#xED; z Qt.\nUmo&#x17E;&#x148;uje tak efektivn&#x11B; zpracov&#xE1;vat Qt ud&#xE1;losti z&#xE1;rove&#x148; s asynchronn&#xED;mi funkcemi\nzn&#xE1;m&#xFD;mi z <code>asyncio</code>.</p>\n<p><em>Event loop</em> z <code>quamash</code> je pot&#x159;eba na za&#x10D;&#xE1;tku programu naimportovat a nastavit\njako hlavn&#xED; smy&#x10D;ku ud&#xE1;lost&#xED;, a pot&#xE9; ji, m&#xED;sto Qt-ovsk&#xE9;ho <code>app.exec()</code>, spustit.\nJednotliv&#xE9; asynchronn&#xED; funkce se pak pou&#x17E;&#xED;vaj&#xED; jako v &#x10D;ist&#xE9;m <code>asyncio</code>:\npomoc&#xED; <code>asyncio.ensure_future</code>, <code>await</code>, atd.</p>\n<p>Uk&#xE1;zka:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">asyncio</span>\n\n<span class=\"kn\">from</span> <span class=\"nn\">PyQt5</span> <span class=\"kn\">import</span> <span class=\"n\">QtGui</span><span class=\"p\">,</span> <span class=\"n\">QtWidgets</span>\n<span class=\"kn\">from</span> <span class=\"nn\">quamash</span> <span class=\"kn\">import</span> <span class=\"n\">QEventLoop</span>\n\n<span class=\"n\">app</span> <span class=\"o\">=</span> <span class=\"n\">QtWidgets</span><span class=\"o\">.</span><span class=\"n\">QApplication</span><span class=\"p\">([])</span>\n<span class=\"n\">loop</span> <span class=\"o\">=</span> <span class=\"n\">QEventLoop</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n<span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">set_event_loop</span><span class=\"p\">(</span><span class=\"n\">loop</span><span class=\"p\">)</span>\n\n<span class=\"n\">display</span> <span class=\"o\">=</span> <span class=\"n\">QtWidgets</span><span class=\"o\">.</span><span class=\"n\">QLCDNumber</span><span class=\"p\">()</span>\n<span class=\"n\">display</span><span class=\"o\">.</span><span class=\"n\">setWindowTitle</span><span class=\"p\">(</span><span class=\"s1\">&apos;Stopwatch&apos;</span><span class=\"p\">)</span>\n\n<span class=\"n\">display</span><span class=\"o\">.</span><span class=\"n\">show</span><span class=\"p\">()</span>\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">update_time</span><span class=\"p\">():</span>\n    <span class=\"n\">value</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"n\">display</span><span class=\"o\">.</span><span class=\"n\">display</span><span class=\"p\">(</span><span class=\"n\">value</span><span class=\"p\">)</span>\n        <span class=\"n\">await</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n        <span class=\"n\">value</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n\n<span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">ensure_future</span><span class=\"p\">(</span><span class=\"n\">update_time</span><span class=\"p\">())</span>\n\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">run_forever</span><span class=\"p\">()</span>\n</pre></div><h2>Komunikace</h2>\n<p>Ono <code>io</code> v <code>asyncio</code> nazna&#x10D;uje, &#x17E;e je tato knihovna d&#x11B;lan&#xE1; p&#x159;edev&#x161;&#xED;m na\nvstup a v&#xFD;stup &#x2013; konkr&#xE9;tn&#x11B; na komunikaci p&#x159;es s&#xED;&#x165; (p&#x159;&#xED;padn&#x11B; s jin&#xFD;mi procesy).</p>\n<p>Ke komunikaci pou&#x17E;&#xED;v&#xE1; <code>asyncio</code> t&#x159;i &#xFA;rovn&#x11B; abstrakce: <code>Transport</code>, <code>Protocol</code>\na <code>Stream</code>.\nV kr&#xE1;tkosti si je tu pop&#xED;&#x161;eme; detaily pak najdete v dokumentaci (je pro n&#xE1;s\ntoti&#x17E; mnohem d&#x16F;le&#x17E;it&#x11B;j&#x161;&#xED; abyste pochopili principy, ne&#x17E; abyste um&#x11B;li konkr&#xE9;tn&#xED;\nAPI, kter&#xE9; lze dohledat v dokumentaci).</p>\n<p>Transporty a protokoly jsou postaveny na konceptech knihovny <code>Twisted</code>.</p>\n<p><code>Transport</code> zaji&#x161;&#x165;uje samotn&#xE9; pos&#xED;l&#xE1;n&#xED; bajt&#x16F; mezi po&#x10D;&#xED;ta&#x10D;i (transportn&#xED; vrstvu), kde&#x17E;to\n<code>Protocol</code> implementuje n&#x11B;jak&#xFD; aplika&#x10D;n&#xED; protokol.\n<code>Transport</code> v&#x11B;t&#x161;inou nep&#xED;&#x161;eme sami, pou&#x17E;ijeme existuj&#xED;c&#xED;.\nV <code>asyncio</code> jsou zabudovan&#xE9; transporty pro TCP, UDP a SSL.\n<code>Protocol</code> je pak pou&#x17E;it pro implementaci konkr&#xE9;tn&#xED;ch protokol&#x16F; jako\n<code>HTTP</code>, <code>FTP</code> a podobn&#x11B;.\nV dokumentaci najdete podrobn&#x11B;j&#x161;&#xED; popis v&#x10D;etn&#x11B; <a href=\"https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server-protocol\">p&#x159;&#xED;klad&#x16F;</a>.</p>\n<p>Krom&#x11B; toho existuje i &#x201E;Stream API&#x201C; zalo&#x17E;en&#xE9; na asynchronn&#xED;ch funkc&#xED;ch.\nV&#x11B;t&#x161;inou plat&#xED;, &#x17E;e operace <em>otev&#x159;en&#xED;</em>, <em>&#x10D;ten&#xED;</em>, <em>flush</em> a <em>zav&#x159;en&#xED;</em> Streamu\njsou asynchronn&#xED; funkce (v dokumentaci ozna&#x10D;ovan&#xE9; jako <em>coroutines</em>), a je\ntedy nutn&#xE9; je pou&#x17E;&#xED;t s <code>await</code>; oproti tomu <em>z&#xE1;pis</em> asynchronn&#xED; nen&#xED; &#x2013; data\nse ulo&#x17E;&#xED; do bufferu a po&#x161;lou se, a&#x17E; to bude mo&#x17E;n&#xE9;.</p>\n<p>Typicky ale m&#xED;sto &#x10D;ist&#xE9;ho <code>asyncio</code> pou&#x17E;ijeme existuj&#xED;c&#xED; knihovnu.\nTady je p&#x159;&#xED;klad z knihovny <code>aiohttp</code>, kter&#xE1; implementuje server a klienta\npro HTTP:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">asyncio</span>\n<span class=\"kn\">import</span> <span class=\"nn\">aiohttp</span>\n\n<span class=\"n\">async</span> <span class=\"k\">def</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">):</span>\n    <span class=\"c1\"># Use a a session</span>\n    <span class=\"n\">session</span> <span class=\"o\">=</span> <span class=\"n\">aiohttp</span><span class=\"o\">.</span><span class=\"n\">ClientSession</span><span class=\"p\">()</span>\n    <span class=\"n\">async</span> <span class=\"k\">with</span> <span class=\"n\">session</span><span class=\"p\">:</span>\n\n        <span class=\"c1\"># Get the response (acts somewhat like a file; needs to be closed)</span>\n        <span class=\"n\">async</span> <span class=\"k\">with</span> <span class=\"n\">session</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">response</span><span class=\"p\">:</span>\n\n            <span class=\"c1\"># Fetch the whole text</span>\n            <span class=\"n\">html</span> <span class=\"o\">=</span> <span class=\"n\">await</span> <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">()</span>\n            <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">)</span>\n\n<span class=\"n\">loop</span> <span class=\"o\">=</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">get_event_loop</span><span class=\"p\">()</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">run_until_complete</span><span class=\"p\">(</span><span class=\"n\">main</span><span class=\"p\">(</span><span class=\"s1\">&apos;http://python.cz&apos;</span><span class=\"p\">))</span>\n<span class=\"n\">loop</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n</pre></div>\n\n\n        "
    }
  }
}