Na tomto cvičení budete upravovať jeden súbor.
Implementujte plánovač pre koprogramy implementované pomocou rozšírených generátorov. Plánovač vykonáva koprogramy algoritmom ide pieseň dokola (round robin). Po ukončení úlohy plánovač vypíše informatívny výpis.
Koprogramy môžu vrátiť (pomocou yield) niekoľko špeciálnych príkazov, ktoré musí plánovač rozpoznať a spracovať:
SleepSendToImplementáciu plánovača dokončite podľa TODO komentárov.
Na ukážku implementujte aspoň tri koprogramy:
Na záver plánovač otestujte s implementovanými koprogramami pre niekoľko kôl.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
"""Asynchronny planovac. Kod pripravil Anthropic (2026), Claude.""" from dataclasses import dataclass, field from collections import deque from typing import Any def consumer(func): """Dekorator, ktory automaticky aktivuje koprogram.""" def wrapper(*args, **kw): it = func(*args, **kw) next(it) return it wrapper.__name__ = func.__name__ wrapper.__dict__ = func.__dict__ wrapper.__doc__ = func.__doc__ return wrapper # ----------------------------------------------------------------------------- # Prikazy # ------- # Koprogramy mozu vratit (pomocou yield) planovacu niektore z tychto tried, # ktore maju specialny vyznam # ----------------------------------------------------------------------------- @dataclass class Sleep: """Pozastav koprogram na dany pocet kol.""" rounds: int @dataclass class SendTo: """Posli hodnotu do schranky ineho koprogramu. Planovac doruci hodnotu koprogramu `job_id` pri nasledujucom kroku toho koprogramu. """ job_id: int value: Any # ----------------------------------------------------------------------------- # Reprezentacia ulohy v planovaci # ----------------------------------------------------------------------------- @dataclass class Job: """Uloha pre planovac.""" job_id: int "Jedinecny identifikator ulohy." iterator: Any "Generatorovy iterator koprogramu." sleep_until: int = 0 """Cislo kola, od ktoreho je uloha znovu pripravena bezat; predvolena hodnota 0 znamena 'hned pripravena'. """ inbox: deque = field(default_factory=deque) "Front hodnot posielanych tejto ulohe cez SendTo." # ----------------------------------------------------------------------------- # Planovac # ----------------------------------------------------------------------------- class Scheduler: """Planovac koprogramov zalozeny na rozsirenych generatoroch. Algoritmus (round-robin): Kazde kolo planovac prejde vsetkymi ulohami, ktore su pripravene na beh (ktore aktualne nespia), a kazdu posunie o jeden krok. Po spracovani vsetkych pripravnych uloh zvysi pocitadlo kola o 1. Koprogramy mozu vratit hodnoty: None -- vzdaj sa CPU na jedno kolo Sleep(n) -- pozastav sa na `n` kol SendTo(job_id, value) -- posli hodnotu `value` ulohe `job_id` Ak ma uloha v schranke cakajuce hodnoty, planovac jej posle najstarsiu hodnotou. Ked koprogram skonci (vyvola StopIteration), planovac vypise informaciu a odstrani ulohu. """ def __init__(self): # TODO: Implementujte metodu. pass def add_job(self, it) -> int: """Zaregistruje novy koprogram do planovaca. :param it: koprogram (generatorovy iterator) :returns: jedinecny identifikator novej ulohy TODO: Implementujte metodu: 1. Vygenerujte novy jedinecny identifikator ulohy. 2. Vytvorte instanciu Job a ulozte ju. 3. Vratte identifikator ulohy. """ pass def resume(self, rounds: int = -1) -> None: """Spusti beh planovaca na `rounds` kol. :param rounds: pocet kol na vykonanie; -1 znamena bezat dovtedy, kym existuje aspon jedna uloha Planovac v kazdej iteracii vykona koprogramy, ktore su pripravene na beh (nespia). Pomocka: Zoznam pripravnych uloh vypocitajte na ZACIATKU kazdeho kola, nie pocas iterovania -- v priebehu kola mozu pribudnut nove ulohy. TODO: Implementujte metodu. """ pass def _step(self, job: Job) -> None: """Posunie jednu ulohu o jeden krok. Logika: - Ak job.inbox nie je prazdny, zoberte najstarsiu hodnotu a vyvolajte koprogram s touto hodnotou na vstupe. - Inak vyvolajte koprogram bez vstupnej hodnoty. - Spracujte ukoncenie koprogramu. Po ukonceni vypiste informacnu spravu (napr. "[SCHED] Job X finished."). - Vratenu hodnotu z koprogramu spracujte metodou `_handle_command()`. TODO: Implementujte metodu. """ pass def _handle_command(self, job: Job, cmd) -> None: """Spracuje prikaz z koprogramu.""" # TODO: Implementujte metodu. pass # ----------------------------------------------------------------------------- # Koprogramy # ---------- # TODO: Implementujte aspon TRI koprogramy pomocou rozsirenych generatorov. # Aspon JEDEN musi byt nekonecny. # Aspon JEDEN musi byt konecny. # Aspon JEDEN musi posielat druhemu koprogramu data. # Aspon JEDEN musi spracovavat data z ineho koprogramu. # ----------------------------------------------------------------------------- def main(): """Ukazka cinnosti planovaca s koprogramami.""" sched = Scheduler() # TODO: Vytvorte koprogramy a spustite planovac na niekolko kol. if __name__ == '__main__': main() |
