Naučení se asynchronnímu programování

Re:Naučení se asynchronnímu programování
« Odpověď #105 kdy: 12. 10. 2019, 10:48:09 »
Z epollu se samozrejme cte ve smycce, ale tady je celou dobu rec o Event Loop designu jak to ma Javacript, Node.js, Vertex, atp., a ne doprcic o tom, ze sis nekde v programu udelal while(true) a ctes neco ve worker threadu pres epoll.

To "e" v epoll znamena "event". Ta samozrejma smycka na cteni je "loop". Nahoda?


Idris

  • *****
  • 2 286
    • Zobrazit profil
    • E-mail
Re:Naučení se asynchronnímu programování
« Odpověď #106 kdy: 12. 10. 2019, 10:50:10 »
Z epollu se samozrejme cte ve smycce, ale tady je celou dobu rec o Event Loop designu jak to ma Javacript, Node.js, Vertex, atp., a ne doprcic o tom, ze sis nekde v programu udelal while(true) a ctes neco ve worker threadu pres epoll.
Jasně, ovčáčku.

Re:Naučení se asynchronnímu programování
« Odpověď #107 kdy: 12. 10. 2019, 11:58:05 »
Z epollu se samozrejme cte ve smycce, ale tady je celou dobu rec o Event Loop designu jak to ma Javacript, Node.js, Vertex, atp., a ne doprcic o tom, ze sis nekde v programu udelal while(true) a ctes neco ve worker threadu pres epoll.

Neztrapňuj se. S tím už jsi dávno hotový.

Re:Naučení se asynchronnímu programování
« Odpověď #108 kdy: 12. 10. 2019, 16:31:10 »
Omlouvám se za trochu delší text, ale je to tak trochu v rámci samostudia :)

Začnu trochu od konce,s epoll-em

Doufám že se shodnome na tom že e to vlastně jen takový rychlejší select/poll a v nejjednodušším případě se s ním pracuje v jednom vlákně. Dále také tak trochu předpokládám neblokující sockety.

Aby to dobře fungovalo pro všechna spojení, tak musí být obsluha rychlá a neblokující.

V každém trochu složitějším případě je práce v epoll smyčce řízená nějakým stavovým automatem. Např SSL_read může požadovat opakované volání (s návratem z volané funkce až k epoll s požadavkem na čekání až bude socket připraven na čtení nebo zápis) dokud není ssl spojení kompletně navázáno.
Podobně také může být třeba se opakovaně vracet až k epoll pokud třeba není vyčten celý příchozí povel atp.(záleží na protokolu).

Pokud se pracuje s jinými prostředky (např ty soubory, db, ..)  tak také nechcete brzdit epoll smyčku synchroním voláním. Např v tom vašem příkladu jste operaci ukládání na disk odsunul do samostatného vlákna, ale tím jste si přidal práci s frontou, předáváním dat mezi vlákny atp. Co když potřebujete předat zprávu zpět z toho externího vlákna? Vrátíte zrávu zpět do epoll vlákna, nebo si budete socket pollovat ve worker threadu?
Věci se mohou začít kompikovat pokud si nedáte pozor, nebo pokud je protokol složitější, případně uchovává složitější stav.

Souhrn:
- V hlavní smyčce nesmí být synchroní/dlouhotrvající operace
- stavové automaty, pokud si nedáte pozor mohou být peklo.
- prakticky nutnost předávání dat mezi vlákny - potenciální problém
- řízení uvolňování zdrojů, a předávání zpráv o chybách mezi vlákny a stavovými automaty může být problém.
+- Trochu to nutí programátora si aplikaci rozdělit
- škálování do více vláken může být problém (pro každou databázi/disk/... jedno vlákno)?

Pokud je úloha jednoduchá, může být řešení s epoll krásně přehledné, v opačném případě je třeba uvážlivě členit aplikaci.

Tak a teď ohledně async/await  (z pohledu c#)
Je to sice trochu složitější, ale dá se na to koukat jako na ten stavový automat co se používá u epollu.

Takové await se pak jakoby vrací z funkce do hlavního loopu (podobně jako by se vracelo k epollu) a "vrátí" se až když jsou data dostupná (např načtena dat přes socket) nebo pokud vznikla chyba.
Akorát je to implementováno trochu chytřeji, a neomezuje jen na sockety a async await je možný třeba i při přístupu k disku, databázi, nebo je možné snadno vrstvit třeba protokoly (TCP/IP + SSL + HTTP)
Trochu neplechu může vyvolat pokud se do práce vloží více vláken z ThreadPoolu, je třeba si pohlídat ConfigureAwait.
Ale s trochou zkušeností stačí dodržovat pár základních pravidel (podobně jako musíte dodržovat proavidla při tvorbě stavových automatů pro epoll).

S tím také trochu souvisí pravidlo, že pokud se používá async/await tak všechen (potenciálně blokující) kód by měl používat async/await, v extrémním případě spustit "blokující" kód v threadpoolu.
Navíc je nutné(pokud si nechcete zahrávat s deadlocky) používat await celou cestu vzhůru, a awaitovat výsledek namísto blokujícího task.Result a podobně.(což zjednodušeně znamený že i první funkce na stakcu měla být async, například main)
To je důvod proč možná narážíte na to že někdo "zbytečně" používá async/await.
Když se nad tím zamyslíte, tak je to vlastně logické, synchroní kód v hlavní "smyčce" blokuje provádění ostatních "Tasků" (ačkoliv zde je to trochu složitější, a špatný kód může "občas/často" fungovat nebo nefungovat kvůli připadnému provádění nebo neprovádění tasků v různých vláknech).

Souhrn:
- V hlavní smyčce nesmí být synchroní/dlouhotrvající operace
+ umí jednoduše asynchroně pracovat i s dalšími prostředky (soubory, připojení k databázi, uživatelské funkce používající async,..), ne jenom se sockety, to třeba umožňuje pěkně zapouzdřit komunikační protokol atd.
+ stavový automat za vás vygeneruje překladač (ačkoliv je dobré to tušit kvůli úspoře prostředků)
+- koncept async/await umožňuje v některých případech vyhnout se explicitnímu vytváření vláken úplně.
+- řízení uvolňování zdrojů, a předávání zpráv o chybách - celkem jednoduché jako v "synchroním" kódu, pokud
explicitně nepředáváte do workerthreadů zprávy jako v prvnímpřípadě.
+škálování do více vláken nemusí být problém
- Pokud nedodržíte základní pravidla (jako např. async/await all the way up, a pohlídat si ConfigureAwait) může kód fungovat díky shodě náhod a použitému synchronizačnímu kontextu(věc z C#).
- Pokud se věci zpracovávají na Threadpollu, je třeba si uvědomit že není nekonečný :)
- může být nutno explicině přiškrtit některé operace (což v předhozím případě mohl zajišťovat worker thread)
+ Stále je možno používat worker thready jako v předchozím případě, pokud to dává smysl.

Pro jednoduchou aplikaci, je to jednoduché, pro složitější trochu víc, ale víceméně to vypadá jako synchroní kód(s tím že je vidět kde stavový automat může navrátit řízení do "hlavní smyčky".
Pro oba případy je ale třeba dodržet základní pravidla.