Ebből az oktatóanyagból megtudhatja, hogyan használhatja a Python beépített szálfűző modulját a Python többszálas képességeinek felfedezéséhez.
A folyamatok és szálak alapjaitól kezdve megtanulhatja, hogyan működik a többszálú feldolgozás Pythonban – miközben megérti a párhuzamosság és a párhuzamosság fogalmát. Ezután megtanulhatja, hogyan indítson el és futtasson egy vagy több szálat a Pythonban a beépített szálfűző modul segítségével.
Kezdjük el.
Tartalomjegyzék
Folyamatok vs. szálak: mi a különbség?
Mi az a folyamat?
A folyamat egy program bármely példánya, amelynek futnia kell.
Bármi lehet – Python-szkript vagy webböngésző, például Chrome egy videokonferencia-alkalmazáshoz. Ha elindítja a Feladatkezelőt a gépen, és a Teljesítmény -> CPU elemre navigál, akkor láthatja a CPU magokon jelenleg futó folyamatokat és szálakat.
A folyamatok és szálak megértése
Belsőleg egy folyamatnak van egy dedikált memóriája, amely a folyamatnak megfelelő kódot és adatokat tárolja.
Egy folyamat egy vagy több szálból áll. A szál az utasítások legkisebb sorozata, amelyet az operációs rendszer végrehajthat, és a végrehajtás folyamatát reprezentálja.
Minden szálnak megvan a maga verem és regiszter, de nincs dedikált memória. A folyamathoz kapcsolódó összes szál hozzáférhet az adatokhoz. Ezért az adatokat és a memóriát a folyamat összes szála megosztja.
Egy N maggal rendelkező CPU-ban N folyamat tud párhuzamosan végrehajtani ugyanabban az időben. Ugyanazon folyamat két szála azonban soha nem futhat párhuzamosan, de párhuzamosan. Az egyidejűség és a párhuzamosság fogalmával a következő részben foglalkozunk.
Az eddig tanultak alapján foglaljuk össze a folyamat és a szál közötti különbségeket.
FeatureProcessThreadMemory Dedikált memóriaMegosztott memória VégrehajtásmódPárhuzamos, párhuzamos Egyidejű; de nem párhuzamos végrehajtás, amelyet az Operating SystemCPython Interpreter kezel
Többszálú feldolgozás Pythonban
A Pythonban a Global Interpreter Lock (GIL) biztosítja, hogy csak egy szál tudja megszerezni a zárolást és futni bármikor. Minden szálnak meg kell szereznie ezt a zárolást a futáshoz. Ez biztosítja, hogy egy adott időpontban csak egyetlen szál legyen végrehajtásban, és elkerülhető az egyidejű többszálú feldolgozás.
Vegyünk például ugyanannak a folyamatnak két szálát, a t1-et és a t2-t. Mivel a szálak ugyanazokat az adatokat osztják meg, amikor t1 egy adott k értéket olvas, t2 módosíthatja ugyanazt a k értéket. Ez holtpontokhoz és nemkívánatos eredményekhez vezethet. De csak az egyik szál szerezheti meg a zárolást és futhat bármely példányban. Ezért a GIL gondoskodik a menetbiztonságról is.
Tehát hogyan érhetjük el a többszálú képességeket a Pythonban? Ennek megértéséhez beszéljük meg a párhuzamosság és a párhuzamosság fogalmát.
Egyidejűség vs. párhuzamosság: áttekintés
Vegyünk egynél több magos CPU-t. Az alábbi ábrán a CPU négy magból áll. Ez azt jelenti, hogy egy adott pillanatban négy különböző művelet futhat párhuzamosan.
Ha négy folyamat van, akkor mindegyik folyamat önállóan és egyidejűleg futhat mind a négy magon. Tegyük fel, hogy minden folyamatnak két szála van.
A szálkezelés működésének megértéséhez váltsunk többmagos processzor architektúráról egymagos architektúrára. Mint említettük, csak egyetlen szál lehet aktív egy adott végrehajtási példányon; de a processzormag tud váltani a szálak között.
Például az I/O-hoz kötött szálak gyakran várnak az I/O műveletekre: olvasás a felhasználói bevitelben, adatbázis-olvasás és fájlműveletek. Ez alatt a várakozási idő alatt feloldhatja a zárat, hogy a másik szál futhasson. A várakozási idő egyszerű művelet is lehet, például n másodperces alvás.
Összefoglalva: A várakozási műveletek során a szál feloldja a zárolást, lehetővé téve a processzormag számára, hogy másik szálra váltson. A korábbi szál végrehajtása a várakozási időszak letelte után folytatódik. Ez a folyamat, ahol a processzormag egyidejűleg vált a szálak között, megkönnyíti a többszálú feldolgozást. ✅
Ha folyamatszintű párhuzamosságot kíván megvalósítani az alkalmazásában, fontolja meg inkább a többfeldolgozás használatát.
Python Threading modul: Első lépések
A Python egy szálfűző modult tartalmaz, amelyet importálhat a Python-szkriptbe.
import threading
Szálobjektum létrehozásához Pythonban használhatja a Thread konstruktort: threading.Thread(…). Ez az általános szintaxis, amely elegendő a legtöbb szálfűzési megvalósításhoz:
threading.Thread(target=...,args=...)
Itt,
- A target a kulcsszó argumentuma, amely egy Python hívható elemet jelöl
- Az args az argumentumok sorozata, amelyet a célpont befogad.
Az oktatóanyagban szereplő kódpéldák futtatásához Python 3.x-re lesz szüksége. Töltse le a kódot, és kövesse a lépést.
Szálak meghatározása és futtatása Pythonban
Definiáljunk egy szálat, amely egy célfüggvényt futtat.
A célfüggvény a some_func.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() print(threading.active_count())
Elemezzük, mit csinál a fenti kódrészlet:
- Importálja a szálfűzést és az időmodulokat.
- A some_func függvénynek leíró print() utasításai vannak, és két másodperces elalvási műveletet tartalmaz: a time.sleep(n) hatására a függvény n másodpercig alvó állapotba kerül.
- Ezután definiálunk egy szálszálat, amelynek a cél a some_func. threading.Thread(target=…) létrehoz egy szál objektumot.
- Megjegyzés: A függvény nevét adja meg, ne függvényhívást; használd a some_func-ot és ne a some_func().
- Szálobjektum létrehozása nem indít el szálat; a start() metódus meghívása a szál objektumon megteszi.
- Az aktív szálak számának meghatározásához az active_count() függvényt használjuk.
A Python szkript fut a fő szálon, és létrehozunk egy másik szálat (thread1) a some_func függvény futtatásához, így az aktív szálak száma kettő, amint az a kimeneten látható:
# Output Running some_func... 2 Finished running some_func.
Ha közelebbről megnézzük a kimenetet, azt látjuk, hogy a thread1 indításakor lefut az első print utasítás. Az alvó üzemmódban azonban a processzor a fő szálra vált, és kinyomtatja az aktív szálak számát – anélkül, hogy megvárná, amíg a szál1 végrehajtása befejeződik.
Várakozás a szálak végrehajtásának befejezésére
Ha azt szeretné, hogy a thread1 befejezze a végrehajtást, a szál elindítása után hívhatja meg rajta a join() metódust. Ezzel megvárja, amíg az 1. szál befejezi a végrehajtást anélkül, hogy a fő szálra váltana.
import threading import time def some_func(): print("Running some_func...") time.sleep(2) print("Finished running some_func.") thread1 = threading.Thread(target=some_func) thread1.start() thread1.join() print(threading.active_count())
Most a thread1 végrehajtása befejeződött, mielőtt kinyomtatjuk az aktív szálak számát. Tehát csak a főszál fut, ami azt jelenti, hogy az aktív szálak száma egy. ✅
# Output Running some_func... Finished running some_func. 1
Több szál futtatása Pythonban
Ezután hozzunk létre két szálat két különböző funkció futtatásához.
Itt a count_down egy olyan függvény, amely egy számot vesz fel argumentumként, és ettől a számtól számol vissza nulláig.
def count_down(n): for i in range(n,-1,-1): print(i)
Meghatározzuk a count_up, egy másik Python-függvényt, amely nullától egy adott számig számol.
def count_up(n): for i in range(n+1): print(i)
📑 Ha a range() függvényt a szintaxistartománnyal (start, stop, step) használja, akkor a végpont stop alapértelmezés szerint ki van zárva.
– Ha egy adott számról nullára szeretne visszaszámolni, használhat -1 negatív lépésértéket, és állítsa a stop értéket -1-re, hogy a nulla szerepeljen.
– Hasonlóképpen, ha n-ig akar számolni, a stop értéket n + 1-re kell állítani. Mivel a start és step alapértelmezett értéke 0, illetve 1, a tartomány(n + 1) segítségével megkaphatja a 0 sorozatot. n-en keresztül.
Ezután definiálunk két szálat, a szál1-et és a szál2-t a count_down és a count_up függvények futtatásához. Mindkét funkcióhoz nyomtatási utasításokat és alvási műveleteket adunk.
A szálobjektumok létrehozásakor vegye figyelembe, hogy a célfüggvény argumentumait sorként kell megadni – az args paraméterhez. Mivel mindkét függvény (count_down és count_up) egy argumentumot vesz fel. Az érték után kifejezetten vesszőt kell beszúrnia. Ez biztosítja, hogy az argumentum továbbra is sorként kerüljön átadásra, mivel a következő elemeket a rendszer None-ként értelmezi.
import threading import time def count_down(n): for i in range(n,-1,-1): print("Running thread1....") print(i) time.sleep(1) def count_up(n): for i in range(n+1): print("Running thread2...") print(i) time.sleep(1) thread1 = threading.Thread(target=count_down,args=(10,)) thread2 = threading.Thread(target=count_up,args=(5,)) thread1.start() thread2.start()
A kimenetben:
- A count_up függvény a 2. szálon fut, és 5-ig számol 0-tól kezdve.
- A count_down függvény az 1. szálon fut, 10-ről 0-ra visszaszámlál.
# Output Running thread1.... 10 Running thread2... 0 Running thread1.... 9 Running thread2... 1 Running thread1.... 8 Running thread2... 2 Running thread1.... 7 Running thread2... 3 Running thread1.... 6 Running thread2... 4 Running thread1.... 5 Running thread2... 5 Running thread1.... 4 Running thread1.... 3 Running thread1.... 2 Running thread1.... 1 Running thread1.... 0
Látható, hogy a szál1 és a szál2 felváltva fut, mivel mindkettő várakozási műveletet (alvó üzemmódot) foglal magában. Miután a count_up függvény befejezte az 5-ig való számlálást, a szál2 már nem aktív. Így csak a szál1-nek megfelelő kimenetet kapjuk.
Összegezve
Ebben az oktatóanyagban megtanulta, hogyan használhatja a Python beépített szálfűző modulját a többszálú feldolgozás megvalósítására. Íme egy összefoglaló a legfontosabb tudnivalókról:
- A Thread konstruktorral szál objektum hozható létre. A threading.Thread(target=
,args=( ))) létrehoz egy szálat, amely a hívható célt az args-ben megadott argumentumokkal futtatja. - A Python program egy főszálon fut, így a létrehozott szálobjektumok további szálak. Meghívhatja az active_count() függvényt, amely visszaadja az aktív szálak számát bármely példányban.
- Elindíthat egy szálat a start() metódussal a szál objektumon, és megvárhatja, amíg a végrehajtás befejeződik a join() metódussal.
További példákat kódolhat a várakozási idők módosításával, más I/O műveletek kipróbálásával stb. Ügyeljen arra, hogy a közelgő Python-projektjeiben többszálat alkalmazzon. Boldog kódolást!🎉