A korutin Je to podobné vláknu, je to prováděcí řádek s vlastním zásobníkem, vlastními lokálními proměnnými a vlastním ukazatelem na pokyny, ale s konkrétností, že sdílí globální proměnné a jakýkoli jiný prvek s ostatními korutinami.
Musíme však objasnit, že mezi nimi existují rozdíly vlákna a korutiny, hlavní rozdíl je v tom, že program, který používá vlákna, je spouští souběžně, korutiny na druhé straně jsou kolaborativní, kde program, který používá korutiny, spouští pouze jeden z nich a jejich pozastavení je dosaženo, pouze pokud je to výslovně požadováno.
The korutiny Jsou extrémně výkonné, pojďme se podívat, co tento koncept zahrnuje a jak je můžeme použít v našich programech.
Základní pojmy
Všechny funkce související s korutinami v Lua se nacházejí v tabulce korutin, kde je funkce vytvořit () umožňuje nám je vytvořit, má jednoduchý argument a je funkcí s kódem, který spustí korutina, kde její návrat je hodnota typu vlákna, která představuje novou korutinu. I argument pro vytvoření korutiny je někdy anonymní funkce jako v následujícím příkladu:
co = coroutine.create (function () print ("Hello Solvetic") konec)A korutin může mít čtyři různé stavy:
- pozastaveno
- ve spěchu
- mrtví
- normální
Když ho vytvoříme, začne ve stavu přerušeno, což znamená, že korutina se nespustí automaticky při prvním vytvoření. Stav korutiny lze konzultovat následujícím způsobem:
tisk (coroutine.status (co))K tomu, abychom mohli spustit naši korutinu, musíme použít pouze funkci shrnuje (), což interně dělá, je změnit jeho stav z pozastaveného na spuštěný.
coroutine.resume (co)Pokud dáme dohromady celý náš kód a přidáme další řádek, abychom po provedení dotazu zjistili dodatečný stav naší korutiny shrnuje můžeme vidět všechny stavy, kterými prochází:
co = coroutine.create (funkce () print ("Hello Solvetic") konec) print (co) print (coroutine.status (co)) coroutine.resume (co) print (coroutine.status (co))Přejdeme na náš terminál a spustíme náš příklad, podívejme se na výstup našeho programu:
lua coroutines1.lua vlákno: 0x210d880 Suspended Hello Solvetic mrtvýJak vidíme, první dojem z korutiny je hodnota vlákna, pak máme stav pozastaveno, a to je v pořádku, protože toto je první stav při vytváření, pak s shrnuje Spustíme korutinu, pomocí které zprávu vytiskne, a poté bude její stav mrtvíprotože splnilo své poslání.
Coroutiny se na první pohled mohou zdát jako komplikovaný způsob volání funkcí, ale jsou mnohem složitější. Síla téhož spočívá ve velké části funkce výnos () což umožňuje pozastavit běžící korutinu, aby se později obnovila její činnost, podívejme se na příklad použití této funkce:
co = coroutine.create (funkce () pro i = 1,10 tisk ("shrnutí coroutine", i) coroutine.yield () konec konce coroutine.resume (co) coroutine.resume (co) coroutine.resume (co) coroutine .resume (co)Tato funkce bude fungovat, dokud nebude spuštěna první výtěžek, a bez ohledu na to, zda máme cyklus pro, bude se tisknout pouze podle tolika shrnuje Pojďme si pro náš coroutine, pro dokončení se podívejme na výstup přes terminál:
lua coroutines 1. lua 1 2 3 4To by byl výstup přes terminál.
Filtry
Jedním z nejjasnějších příkladů, které vysvětlují korutiny, je případ spotřebitel Y generátor informací. Předpokládejme tedy, že máme funkci, která nepřetržitě generuje některé hodnoty ze čtení souboru, a pak máme další funkci, která je čte, podívejme se na ilustrativní příklad toho, jak by tyto funkce mohly vypadat:
generátor funkcí (), zatímco true do local x = io.read () send (x) end end function consumer () while true do local x = receive () io.write (x, "\ n") end endV tomto případě běží spotřebitel i generátor bez jakéhokoli druhu odpočinku a můžeme je zastavit, pokud již nejsou k dispozici žádné další informace ke zpracování, problémem zde však je, jak synchronizovat funkce Poslat() Y dostávat(), protože každý z nich má svou vlastní smyčku a druhá se považuje za službu s možností volání.
Ale s korutinami lze tento problém vyřešit rychle a snadno pomocí dvojité funkce pokračovat / vydat můžeme zajistit, aby naše funkce fungovaly bez problémů. Když coroutine volá funkci výtěžek, nezadá novou funkci, ale vrátí nevyřízené volání, které může tento stav opustit pouze pomocí resume.
Podobně při volání shrnuje také nespustí novou funkci, vrátí volání čekání na výtěžek, shrnutí tohoto procesu je ten, který potřebujeme k synchronizaci funkcí Poslat() Y dostávat(). Aplikování této operace bychom museli použít dostávat() Aplikovat shrnuje generátoru generovat nové informace a poté Poslat() aplikovat výtěžek Pro spotřebitele se podívejme, jak naše funkce vypadají s novými změnami:
funkce receive () místní stav, hodnota = coroutine.resume (generátor) návratová hodnota koncová funkce send (x) coroutine.yield (x) end gen = coroutine.create (function () while true do local x = io.read () poslat (x) konec konec)Ale stále můžeme náš program dále zlepšovat, a to pomocí filtry, což jsou úkoly, které fungují jako generátory a zároveň spotřebitelé a vytvářejí velmi zajímavý proces transformace informací.
A filtr může udělat shrnuje z generátoru získat nové hodnoty a poté použít výtěžek transformovat data pro spotřebitele. Podívejme se, jak můžeme snadno přidat filtry do našeho předchozího příkladu:
gen = generátor () fil = filtr (gen) spotřebitel (fil)Jak vidíme, bylo to extrémně jednoduché, kde jsme kromě optimalizace našeho programu získali body ve čitelnosti, důležité pro budoucí údržbu.
Korutiny jako iterátory
Jedním z nejjasnějších příkladů generátoru / spotřebitele je iterátory přítomný v rekurzivních cyklech, kde iterátor generuje informace, které budou tělem spotřebovány v rámci rekurzivního cyklu, takže by nebylo nerozumné používat korutiny k psaní těchto iterátorů, dokonce i korutiny mají pro tento úkol speciální nástroj.
Pro ilustraci použití, kterého můžeme využít korutiny„Napíšeme iterátor, který vygeneruje permutace daného pole, to znamená, umístí každý prvek pole na poslední pozici a převrátí jej a poté rekurzivně vygeneruje všechny permutace zbývajících prvků, podívejme se, jak naše původní funkce by byla bez zahrnutí korutin:
funkce print_result (var) pro i = 1, #var do io.write (var [i], "") konec io.write ("\ n") konecNyní to, co děláme, zcela změní tento proces, nejprve změníme print_result () podle výtěžku se podívejme na změnu:
funkce permgen (var1, var2) var2 = var2 nebo # var1 pokud var2 <= 1, pak coroutine.yield (var1) elseToto je však ilustrativní příklad, který ukazuje, jak iterátory fungují Lua nám poskytuje funkci s názvem zabalit který je podobný vytvořitNevrací však coroutine, vrací funkci, která když je volána, shrnuje coroutine. Pak použít zabalit měli bychom používat pouze následující:
permutace funkcí (var) vrací coroutine.wrap (function () permgen (var) end) endObvykle je použití této funkce mnohem jednodušší než vytvořit, protože nám dává přesně to, co potřebujeme, což je jeho shrnutí, je však méně flexibilní, protože nám neumožňuje ověřit stav korutinu vytvořeného pomocí zabalit.
Korutiny v Lua Jsou to extrémně účinný nástroj pro řešení všeho, co souvisí s procesy, které je třeba provádět ruku v ruce, ale při čekání na dokončení toho, kdo poskytuje informace, bychom také mohli vidět jejich využití při řešení složitých problémů týkajících se procesů generátor / spotřebitel a také optimalizaci konstrukce iterátorů v našich programech.