Prečo asyncio v Pythone zápasí so zdieľaným stavom a čo s tým robiť
Asyncio v Pythone pravidelne zápasí s koordináciou asynchrónnych úloh okolo zdieľaného stavu. Najčastejšie používané prvky asyncio.Event a asyncio.Condition majú medzery, ktoré vedú k strate aktualizácií. V článku preskúmame tieto problémy a prezentujeme riešenie využívajúce záznamové fronty na elimináciu chýb.

Problematika zdieľaného stavu v asyncio
V rámci populárneho jazyka Python a jeho knižnice asyncio sa pravidelne stretávame s potrebou koordinácie asynchrónnych úloh v súvislosti so zdieľaným stavom. Hoci štandardná knižnica ponúka nástroje ako asyncio.Event a asyncio.Condition, oba majú svoje nedostatky, ktoré naplno vyvstávajú pri skutočnom tlaku konkurenčných úloh.
Pokusy o riešenie
Prvá myšlienka je použitie pole, teda cyklického kontrolovania aktuálneho stavu. Tento prístup však trpí buď zbytočným zaťažením CPU alebo zbytočnou latenciou. Nasledujúce pokusy zahŕňajú asyncio.Event, ktorý síce eliminuje pole, ale neprakticky proliferuje viaceré udalosti pre rôzne stavy, čo vedie k možným chybám v koordinácii.
Problém 'stratenej' aktualizácie
Najvýznamnejší problém s asyncio.Condition je tzv. 'stratená' aktualizácia, kde rýchlo nasledujúce zmeny stavu môžu prebehnúť skôr, než sa spotrebiteľské úlohy zobudia, reakčne zareagujú a vedome skontrolujú aktuálny stav. To môže viesť k úplnému vynechaniu niektorých premien stavu spotrebiteľskými úlohami.
Riešenie s využitím záznamových front
Jednoúčelové riešenie pre tento problém ponúka vytvorenie záznamových front pre každý spotrebiteľ. Pri zmene stavu sa pre každého spotrebiteľa zaznamená stará a nová hodnota do jeho fronty a spotrebiteľ samostatne vyhodnocuje každý zápis vo fronte.
Ako to nasadiť
Pre finálnu implementáciu je potrebné pridať niekoľko ďalších prvkov, ako sú ochrana pred súbežnými závitmi pomocou threading.Lock, atomická registrácia front a zohľadnenie výpadkov s použitím výnimiek.
Vylepšenie kódu pre produkčné prostredie
Pre použitie v produkčnom prostredí je nevyhnutné pridanie generického typovania, časových obmedzení a metód pre sledovanie zmien, vrátane synchronných sledovacích callbackov. Kompletná implementácia obsahuje približne 300 riadkov kódu, pričom väčšinu tvoria komentáre a užívateľské metódy.