Paralelismus a asynchronnost

Včera jsem měl na Poslední Sobotě přednáškuReact.PHP – klonu node.js napsaném v PHP. React je framework pro asynchronní reaktivní programování. Je postaven na stěžejní premise, že žádná IO operace nikdy neblokuje a výsledný program je řízený událostmi. Takové aplikace můžou být velice škálovatelné, protože nepotřebují armádu vláken, které by většinu času nečinně čekaly na dokončení IO operací. React, stejně jako PHP pracuje v jednom vlákně, ve kterém běží event loop – nekonečná smyčka obsluhující nízkoúrovňové buffery operačního systému. Díky tomu může velice efektivně obsluhovat desítky tisíc spojení najednou1.

Ale o tom teď nechci psát.

Pokud se chcete dozvědět víc jak React funguje, doporučuji dvě přednášky, kde tvůrce Reactu Igor Wiedler, detailně vysvětluje práci asynchronního soukolí.

Teď se chci věnovat něčemu jinému.

Po přednášce (o které si skromně myslím, že se celkem podařila) se sešlo pár dotazů, které by se daly shrnout jako: Jak to může běžet paralelně, když je PHP jenom jednovláknové.

Odpověď je jednoduchá: program neběží paralelně, ale asynchronně a mezi těmito dvěma pojmy je veliký rozdíl.

Slovo paralelní znamená „souběžný, rovnoběžný, probíhající současně“.

Naproti tomu asynchronní znamená „nesynchronizovaný, nesoudobý, nesoučasný, časově nesladěný nebo neprobíhající v uvedeném pořadí“.

Jak říkal Rob Pike, paralelismus vypovídá o provádění více věcí najednou a asynchronnost je záležitost kompozice programu z nezávisle prováděných procesů. Asynchronní věci můžou probíhat na jednom vlákně nebo na několika, ale paralelní podle definice znamená, že se vykonává víc úloh v jeden okamžik.

Jediné co v Reactu probíhá paralelně jsou IO operace, kdy operační systém plní najednou mnoho bufferů spojených s jednotlivými síťovými sockety. Ale to není podstata věci, protože kdyby se OS v jeden okamžik věnoval jenom jednomu spojení, na chování programu by se nic nezměnilo. Stejně tak by se nic nezměnilo, kdyby jsme asynchronní program začali vykonávat v mnohovláknovém prostředí2.

Shrnuto a podtrženo:

Asynchronnost vypovídá o struktuře programu, paralelismus o způsobu jeho vykonávání.


Slajdy z přednášky zde:

Videozáznam zde:

(varování: mluvím velice rychle a divoce gestikuluji)


1 Na jednom serveru se dá zvládnout klidně deset milionů současných spojení, ale na to potřebuje člověk mnohem víc odvahy.
2 Za předpokladu, že všechny operace budou atomické a viditelné okamžitě všem ostatním vláknům.

Flattr this!

This entry was posted in Async. Bookmark the permalink.

5 Responses to Paralelismus a asynchronnost

  1. Pořád nechápu jednu věc, jak řekneš systému ať něco stahuje, nebo zapisuje, že si zatím půjdeš dělat něco jiného?

    • paranoiq says:

      otevřeš neblokující socket a čas od času zeptáš, jestli už je hotovo

      pokud jde o stahování, jde to i bez Reactu. je pro to podpora rovnou v CURLu – https://github.com/…lManager.php (zkoušel jsem nejvýš 1000 paralelních vláken)

    • Honza Tvrdík says:

      Koukni na funkce stream_select & stream_set_bloc­king. Nicméně napsal pomocí toho něco asynchronně bez nějaké abstrakce je dost nepohodlné. Je to skoro jako psát to v C =)

      Zkusil jsem schválně napsat jednoduchý asynchronní downloader, aby bylo vidět, co přibližně React.PHP dělá (resp. co si tak myslím že asi dělá).

  2. Jakub Vrána says:

    Díky za nasdílení slajdů. Mrzí mě, že jsem na přednášce nebyl. Mám otázku ke slajdům 60 a 61: Existuje způsob, jak flow() přimět k tomu, aby čekal na dvě věci najednou? Třeba bychom chtěli najednou stahovat followers a following. Tedy zavolám fetchProfile(), na to potřebuji počkat. Pak chci zavolat fetchFollowers() a fetchFollowing(), ty na sebe vzájemně čekat nemusí, ale na konci chci vrátit třeba jejich průnik, takže musím počkat na obě. Dalo by se něco takového s flow() napsat?

    Zcela nesouvisející věc: Na flow() mi přijde trochu matoucí, že po yieldnutí nePromise generátor dál pokračuje v běhu. Jednak tím pádem neexistuje způsob, jak generátor zastavit a jednak když pak yieldnu něco dalšího, tak dříve yieldnutá hodnota zmizí v propadlišti dějin. Dala by se sémantika z return $recur($x); změnit na return $x;, což by vyřešilo oba problémy?

  3. Flow je z podstaty sekvenční. Když chci dělat víc věcí najednou, musím použít kombinátor When::all (který má signaturu Array[Promise[X]] => Promise[Array[X]])

    $intersection = flow (function () {
      list($flwrs, $flwng) = yield When::all(fetchFollowers(), fetchFollowing());
      yield array_intersect($flwrs, $flwng);
    });

    Druhé otázce úplně nerozumím. Generátor můžu zastavit zevnitř (buď vyhodím výjimku, nebo kód skončí). Generátor ve flow nepředstavuje sekvenci hodnot, ale sekvenci kroků a výpočtů, které vedou k jedné výsledné hodnotě zabalené v Promise. Kdybych mohl yieldnout více hodnot, nestačila by mi promise, ale potřeboval bych něco jako Rx Observable.

Leave a Reply

Your email address will not be published. Required fields are marked *