Többszálú bemenet Python példával: Tanulja meg a GIL-t Python

⚡ Okos összefoglaló

Többszálú bemenet Python több szálat futtat egy folyamaton belül, így azok megosztják a memóriát és egyidejűleg működnek. A szálkezelő modul létrehozza és kezeli ezeket a szálakat, míg a Global Interpreter Lock korlátozza a valódi párhuzamosságot, így a technika a legjobb a bemeneti/kimeneti feladatokhoz.

  • 🔘 Szál vs. folyamat: A szál egy végrehajtási egység egy folyamaton belül, és a szálak futás közben megosztják a folyamat adatait.
  • ☑️ Két modul: Az elavult _thread modul alacsony szintű hívásokat kínál, míg a magasabb szintű szálkezelő modul a szálak építésének modern szabványa.
  • Szálak létrehozása: Bővítsd a Thread osztályt, írd felül a __init__ és a run() függvényeket, majd hívd meg a start() függvényt az indításhoz, és a join() függvényt a befejezésre váráshoz.
  • 🧪 Kerülje a versenyhelyzeteket: A zárolás egyszerre csak egy szálat enged bejutni egy kritikus szakaszba, megakadályozva ezzel a holtpontokat és a megosztott adatok sérülését.
  • 🇧🇷 Értsd meg a GIL-t: A globális interpreter zárolás lehetővé teszi egy szál futtatását Python bájtkód egyszerre, így a CPU-hoz kötött munka többfeldolgozást igényel.
  • 🤖 AI-alapú munkaterhelések: A többszálú feldolgozás felgyorsítja a bemenethez/kimenethez kötött mesterséges intelligencia lépéseket, például az adatbetöltést, míg a NumPy és a PyTorch felszabadítja a GIL-t a párhuzamos számításhoz.

Többszálú bemenet Python

Az Python programozási nyelv lehetővé teszi a többfeldolgozást vagy többszálú működést. Ebben az oktatóanyagban megtanulod, hogyan írhatsz többszálú alkalmazásokat Python.

Mi az a szál?

A szál (thread) a párhuzamos programozás végrehajtási egysége. A többszálú feldolgozás (multithreading) egy olyan technika, amely lehetővé teszi a CPU számára, hogy egy folyamat több feladatát egyszerre hajtsa végre. Ezek a szálak egyénileg is végrehajthatók, miközben megosztják a folyamat erőforrásait.

Mi az a folyamat?

A folyamat alapvetően a végrehajtás alatt álló program. Amikor elindít egy alkalmazást a számítógépén (például egy böngészőt vagy szövegszerkesztőt), az operációs rendszer létrehoz egy folyamat.

Mi az a Multithreading Python?

Többszálú bemenet Python A programozás egy jól ismert technika, amelyben egy folyamat több szála megosztja adatterületét a fő szállal, ami egyszerűvé és hatékonnyá teszi az információmegosztást és a kommunikációt a szálakon belül. A szálak könnyebbek, mint a folyamatok. Több szál futhat külön-külön, miközben megosztják a folyamat erőforrásait. A többszálú feldolgozás célja több feladat és függvény egyidejű futtatása.

Mi az a Multiprocessing?

multiprocessing lehetővé teszi több független folyamat egyidejű futtatását. Ezek a folyamatok nem osztják meg erőforrásaikat, és az IPC-n keresztül kommunikálnak.

Python Multithreading vs Multiprocessing

A folyamatok és szálak megértéséhez vegyünk egy ilyen forgatókönyvet: Egy .exe fájl a számítógépeden egy program. Amikor megnyitod, az operációs rendszer betölti a memóriába, és a CPU végrehajtja. A programnak azt a példányát, amelyik most fut, folyamatnak nevezzük.

Minden folyamatnak két alapvető összetevője van:

  • Az Code
  • Az adat

Most egy folyamat egy vagy több ún. alrészt tartalmazhat szálak. Ez az operációs rendszer architektúrájától függ. A szálat a folyamat egy olyan szakaszaként képzelhetjük el, amelyet az operációs rendszer külön végrehajthat.

Más szóval, ez egy olyan utasításfolyam, amelyet az operációs rendszer függetlenül futtathat. Az egyetlen folyamaton belüli szálak megosztják az adott folyamat adatait, és úgy vannak kialakítva, hogy együttműködve elősegítsék a párhuzamosságot.

Miért használja a Multithreading-et?

A multithreading lehetővé teszi, hogy egy alkalmazást több részfeladatra bontsa, és ezeket a feladatokat egyidejűleg futtassa. Ha megfelelően használja a többszálas megoldást, az alkalmazás sebessége, teljesítménye és renderelése egyaránt javítható.

Python Többszálas

Python támogatja mind a többfeldolgozású, mind a többszálú feldolgozáshoz szükséges konstrukciókat. Ebben az oktatóanyagban elsősorban a megvalósításra fogsz összpontosítani többszálú alkalmazásokkal PythonKét fő modul használható a szálak kezelésére a Python:

  1. Az szál modul, és
  2. Az befűzés modul

Azonban a Pythonlétezik egy úgynevezett globális interpreter lock (GIL) is. Ez nem tesz lehetővé jelentős teljesítménynövekedést, sőt akár csökkenteni egyes többszálú alkalmazások teljesítménye. Az oktatóanyag következő részeiben mindent megtudhat róla.

A Thread és Threading modulok

Az oktatóanyagban megismert két modul a menet modul és a menetvágó modul.

A szálmodul azonban már régóta elavult. Kezdve ezzel Python 3, azt elavultnak minősítették, és csak a következő néven érhető el _szál visszafelé kompatibilitás érdekében.

A magasabb szintet kell használnia befűzés modul a telepíteni kívánt alkalmazásokhoz. A szál modult itt csak oktatási célokból tárgyaltuk.

A szál modul

A szintaxis egy új szál létrehozásához ezzel a modullal a következő:

thread.start_new_thread(function_name, arguments)

Rendben, most lefedte az alapvető elméletet a kódolás megkezdéséhez. Szóval nyisd ki a te IDLE vagy egy jegyzettömböt, és írja be a következőket:

import time
import _thread

def thread_test(name, wait):
   i = 0
   while i <= 3:
      time.sleep(wait)
      print("Running %s\n" %name)
      i = i + 1

   print("%s has finished execution" %name)

if __name__ == "__main__":
    
    _thread.start_new_thread(thread_test, ("First Thread", 1))
    _thread.start_new_thread(thread_test, ("Second Thread", 2))
    _thread.start_new_thread(thread_test, ("Third Thread", 3))

Mentse el a fájlt, és nyomja meg az F5 billentyűt a program futtatásához. Ha minden helyesen történt, akkor ezt a kimenetet kell látnia:

A szál modul

A következő részekben többet is megtudhatsz a versenykörülményekről és azok kezeléséről.

A szál modul

KÓDMAGYARÁZAT

  1. Ezek az utasítások importálják az idő és a szál modult, amelyek a végrehajtás és a késleltetés kezelésére szolgálnak. Python szálak.
  2. Itt egy függvényt definiáltunk thread_test, amelyet a start_new_thread metódus. A függvény egy while ciklust futtat négy iteráción keresztül, és kinyomtatja a hívó szál nevét. Az iteráció befejezése után egy üzenetet nyomtat, amely szerint a szál végrehajtása befejeződött.
  3. Ez a program fő része. Itt egyszerűen hívja a start_new_thread módszerrel a thread_test függvényt argumentumként. Ez egy új szálat hoz létre az argumentumként átadott függvényhez, és elkezdi annak végrehajtását. Fontos megjegyezni, hogy ezt (thread_test) bármilyen más függvénnyel lecserélheted, amelyet szálként szeretnél futtatni.

A menetkészítő modul

Ez a modul a szálkezelés magas szintű implementációja a következőben: Python és a többszálú alkalmazások kezelésének tényleges szabványa. A szálmodulhoz képest széleskörű funkciókat kínál.

A Threading modul felépítése

A Threading modul felépítése

Íme egy lista az ebben a modulban meghatározott hasznos funkciókról:

Funkció neve Leírás
activeCount() A számot adja vissza Szál még élő tárgyak.
currentThread() A Thread osztály aktuális objektumát adja vissza.
felsorolja () Felsorolja az összes aktív szál objektumot.
isDaemon() Igaz értéket ad vissza, ha a szál démon.
életben van() Igaz értéket ad vissza, ha a szál még él.
Szálosztály metódusok
Rajt() Elindítja egy szál tevékenységét. Minden szálhoz csak egyszer kell meghívni, mert többszöri hívás esetén futásidejű hibát dob.
fuss() Ez a metódus egy szál tevékenységét jelöli, és a Thread osztályt kiterjesztő osztállyal felülírható.
csatlakozni () Addig blokkolja a többi kód végrehajtását, amíg a szál, amelyen a join() metódus meghívásra került, meg nem szakad.

Háttértörténet: The Thread Class

Mielőtt elkezdenél többszálú programokat kódolni a szálkezelő modullal, elengedhetetlen a Thread osztály megértése. A szálkezelő osztály az elsődleges osztály, amely meghatározza a szál sablonját és műveleteit a szálban. Python.

A többszálú létrehozásának leggyakoribb módja Python Az alkalmazás lényege, hogy deklaráljunk egy osztályt, amely kiterjeszti a Thread osztályt és felülírja annak run() metódusát.

A Thread osztály összefoglalva egy különálló kódsorozatot jelöl szál az irányítás.

Tehát többszálú alkalmazás írásakor a következőket kell tennie:

  1. definiáljon egy osztályt, amely kiterjeszti a Thread osztályt
  2. Felülírja a __init__ konstruktőr
  3. Felülírja a fuss() módszer

Miután egy szál objektum elkészült, a Rajt() a metódus felhasználható a tevékenység végrehajtásának megkezdésére, és a csatlakozni () metódus használható az összes többi kód blokkolására az aktuális tevékenység befejezéséig.

Most próbáljuk meg a szálkezelő modult használni az előző példád megvalósításához. Indítsd el újra a IDLE és írja be a következőket:

import time
import threading

class threadtester (threading.Thread):
    def __init__(self, id, name, i):
       threading.Thread.__init__(self)
       self.id = id
       self.name = name
       self.i = i
       
    def run(self):
       thread_test(self.name, self.i, 5)
       print ("%s has finished execution " %self.name)

def thread_test(name, wait, i):

    while i:
       time.sleep(wait)
       print ("Running %s \n" %name)
       i = i - 1

if __name__=="__main__":
    thread1 = threadtester(1, "First Thread", 1)
    thread2 = threadtester(2, "Second Thread", 2)
    thread3 = threadtester(3, "Third Thread", 3)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()

Ez lesz a kimenet, amikor végrehajtja a fenti kódot:

Háttértörténet: The Thread Class

KÓDMAGYARÁZAT

Háttértörténet: The Thread Class

  1. Ez a rész megegyezik az előző példánkkal. Itt importálod az idő és a szál modult, amelyek a végrehajtás és a késleltetések kezelésére szolgálnak. Python szálak.
  2. Ebben a bitben egy threadtester nevű osztályt hoz létre, amely örökli vagy kiterjeszti a Szál a szálkezelő modul osztálya. Ez az egyik leggyakoribb módja a szálak létrehozásának a PythonAzonban csak a konstruktort és a fuss() módszert az alkalmazásban. Amint a fenti kódmintán látható, a __init__ metódus (konstruktor) felül lett írva. Hasonlóképpen felülbírálta a fuss() módszer. A szálon belül végrehajtani kívánt kódot tartalmazza. Ebben a példában meghívta a thread_test() függvényt.
  3. Ez a thread_test() metódus, amely a következő értéket veszi fel: i argumentumként, minden iterációban 1-gyel csökkenti, és végigmegy a kód többi részén, amíg az i értéke 0 nem lesz. Minden iterációban kinyomtatja az aktuálisan futó szál nevét, és várakozási másodperceket vár (amit szintén argumentumként vesz fel).
  4. thread1 = threadtester(1, “First Thread”, 1) Itt létrehozunk egy szálat, és átadjuk a három paramétert, amelyeket az __init__-ban deklaráltunk. Az első paraméter a szál azonosítója, a második paraméter a szál neve, a harmadik paraméter pedig a számláló, amely meghatározza, hogy a while ciklus hányszor futjon le.
  5. thread2.start() A start metódus egy szál végrehajtásának elindítására szolgál. Belsőleg a start() függvény meghívja az osztály run() metódusát.
  6. thread3.join() A join() metódus blokkolja más kódok végrehajtását, és megvárja, amíg a szál, amelyen hívták, befejeződik.

Mint már tudjuk, az ugyanabban a folyamatban lévő szálak hozzáférnek az adott folyamat memóriájához és adataihoz. Ennek eredményeként, ha egynél több szál próbálja meg egyszerre módosítani vagy elérni az adatokat, hibák léphetnek fel.

A következő részben bemutatjuk a különböző típusú komplikációkat, amelyek akkor jelentkezhetnek, amikor a szálak a meglévő hozzáférési tranzakciók ellenőrzése nélkül férnek hozzá az adatokhoz és a kritikus szakaszhoz.

Holtpontok és versenyfeltételek

Mielőtt belekezdenénk a holtpontok és a versenyfeltételek megismerésébe, hasznos lesz megérteni néhány alapvető definíciót a párhuzamos programozással kapcsolatban:

  • Kritikus szakasz: Ez egy kódrészlet, amely megosztott változókhoz fér hozzá vagy módosítja azokat, és atomi tranzakcióként kell végrehajtani.
  • Kontextusváltás: Ez az a folyamat, amelyet a CPU követ egy szál állapotának tárolására, mielőtt egyik feladatról a másikra váltana, hogy később ugyanonnan folytatható legyen.

Holtpontok

Holtpontok a legfélelmetesebb problémák, amelyekkel a fejlesztők szembesülnek, amikor párhuzamos/többszálú alkalmazásokat írnak PythonA holtpontok megértésének legjobb módja a klasszikus számítástechnikai példafeladat, az úgynevezett Étkezés Philosophers Probléma.

Az étkezési filozófusok problémafelvetése a következő:

Öt filozófus ül egy kerek asztalnál, öt tányér spagettivel (egyfajta tésztával) és öt villával, ahogy az ábrán látható.

Étkezés Philosophers Probléma

Étkezés Philosophers Probléma

Egy filozófusnak bármikor eszik, vagy gondolkodik.

Sőt, a filozófusnak el kell vennie a mellette lévő két villát (azaz a bal és a jobb villát), mielőtt megeheti a spagettit. A holtpont problémája akkor jelentkezik, ha mind az öt filozófus egyszerre veszi fel a jobb villát.

Mivel mindegyik filozófusnak van egy villája, mindannyian megvárják, míg a többiek leteszik a villát. Ennek eredményeként egyikük sem fog tudni enni spagettit.

Hasonlóképpen, egy párhuzamos rendszerben patthelyzet lép fel, amikor különböző szálak vagy folyamatok (filozófusok) egyszerre próbálják megszerezni a megosztott rendszererőforrásokat (forkokat). Ennek eredményeként egyik folyamat sem kap lehetőséget a végrehajtásra, mivel egy másik folyamat által birtokolt másik erőforrásra vár.

A verseny feltételei

A versenyhelyzet egy program nemkívánatos állapota, amely akkor következik be, amikor egy rendszer két vagy több műveletet hajt végre egyszerre. Vegyük például ezt az egyszerű for ciklust:

i=0; # a global variable
for x in range(100):
    print(i)
    i+=1;

Ha létrehoz n Ha egyszerre több szál futtatja ezt a kódot, nem lehet meghatározni az i értékét (amelyet a szálak megosztanak), amikor a program befejezi a végrehajtást. Ez azért van, mert egy valódi többszálú környezetben a szálak átfedhetik egymást, és az i értéke, amelyet egy szál lekérett és módosított, megváltozhat, amikor egy másik szál hozzáfér hozzá.

Ez a két fő problématípus, amelyek többszálú vagy elosztott rendszerekben előfordulhatnak: Python alkalmazás. A következő részben megtudhatja, hogyan lehet ezt a problémát a szálak szinkronizálásával megoldani.

Synchronizáló szálak

A versenykörülmények, a holtpontok és más, szálon alapuló problémák kezelésére a menetkészítő modul biztosítja a Zár objektum. Az ötlet az, hogy amikor egy szál hozzá akar férni egy adott erőforráshoz, akkor zárolást szerez az adott erőforrás számára. Ha egy szál zárol egy adott erőforrást, más szál sem férhet hozzá addig, amíg a zárolást fel nem oldja. Ennek eredményeképpen az erőforrásban végbemenő változások lesznek, a versenykörülmények pedig elkerülhetők lesznek.

A zár egy alacsony szintű szinkronizálási primitív, amelyet a _szál modul. Egy adott időpontban egy zár két állapotban lehet: zárt or feloldva. Két módszert támogat:

  1. szerez(): Amikor a zárolt állapot feloldott, az acquire() metódus meghívása zárolt állapotra változtatja az állapotot és visszatérési értéket ad. Ha azonban az állapot zárolt, az acquire() metódus hívása blokkolva van, amíg a release() metódust egy másik szál meg nem hívja.
  2. kiadás(): A release() metódus arra szolgál, hogy az állapotot feloldottra állítsa, azaz feloldja a zárolást. Bármelyik szál hívhatja, nem feltétlenül az, amelyik megszerezte a zárat.

Íme egy példa a zárak használatára az alkalmazásaidban. Indítsd el a IDLE és írja be a következőt:

import threading
lock = threading.Lock()

def first_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the first funcion')
        lock.release()

def second_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the second funcion')
        lock.release()

if __name__=="__main__":
    thread_one = threading.Thread(target=first_function)
    thread_two = threading.Thread(target=second_function)

    thread_one.start()
    thread_two.start()

    thread_one.join()
    thread_two.join()

Most nyomja meg az F5 billentyűt. Ilyen kimenetet kell látnod:

Synchronizáló szálak

KÓDMAGYARÁZAT

Synchronizáló szálak

  1. Itt egyszerűen létrehoz egy új zárat a következő hívásával befűzés.Lock() gyári funkció. Belsőleg a Lock() a platform által karbantartott leghatékonyabb konkrét Lock osztály egy példányát adja vissza.
  2. Az első utasításban a zárolást az megszerez() metódus meghívásával szerezheti meg. Ha a zárolást engedélyezte, nyomtat „zár megszerzett” a konzolhoz. Miután a szál futtatni kívánt összes kódja befejeződött, feloldja a zárolást a release() metódus meghívásával.

Az elmélet rendben van, de honnan tudhatod, hogy a zárolás valóban működött? Ha megnézed a kimenetet, látni fogod, hogy mindegyik print utasítás pontosan egy sort nyomtat ki egyszerre. Emlékezzünk vissza, hogy egy korábbi példában a print kimenetei véletlenszerűek voltak, mivel több szál is hozzáfért a print() metódushoz egyszerre. Itt a print függvényt csak a zárolás megszerzése után hívjuk meg. Tehát a kimenetek egyenként, soronként jelennek meg.

A zárakon kívül, Python emellett néhány más mechanizmust is támogat a szálak szinkronizálásának kezelésére, az alábbiak szerint:

  1. RLocks
  2. Semaphores
  3. Feltételek
  4. Események, ill
  5. Akadályok

Global Interpreter Lock (és hogyan kell kezelni)

Mielőtt belemennénk a részletekbe PythonGIL-lel kapcsolatban, definiáljunk néhány kifejezést, amelyek hasznosak lesznek a következő szakasz megértéséhez:

  1. CPU-hoz kötött kód: ez minden olyan kódrészletre vonatkozik, amelyet közvetlenül a CPU hajt végre.
  2. I/O-kötésű kód: ez bármilyen kód lehet, amely az operációs rendszeren keresztül fér hozzá a fájlrendszerhez.
  3. CPython: ez a hivatkozás végrehajtás of Python és a C-ben írt interpreterként írható le és Python (programozási nyelv).

Miben található a GIL Python?

Globális tolmácszár (GIL) in Python A GIL egy folyamatzár vagy mutex, amelyet a folyamatok kezelésekor használnak. Biztosítja, hogy egy szál egyszerre csak egy adott erőforráshoz férhessen hozzá, és megakadályozza az objektumok és bájtkódok egyidejű használatát is. Ez az egyszálú programok teljesítménynövekedésével jár. GIL a következőben: Python nagyon egyszerű és könnyen megvalósítható.

A zár segítségével meg lehet győződni arról, hogy egy adott időpontban csak egy szál férhet hozzá egy adott erőforráshoz.

Az egyik jellemzője Python az, hogy globális zárolást használ minden értelmező folyamaton, ami azt jelenti, hogy minden folyamat a Python maga az értelmező, mint erőforrás.

Például tegyük fel, hogy írtál egy Python egy olyan program, amely két szálat használ a CPU és az 'I/O' műveletek végrehajtásához. A program futtatásakor a következő történik:

  1. Az Python Az interpreter létrehoz egy új folyamatot és elindítja a szálakat.
  2. Amikor a szál-1 elindul, először beszerzi a GIL-t és zárolja.
  3. Ha a 2. szál most végre akar hajtani, akkor meg kell várnia a GIL kiadását, még akkor is, ha egy másik processzor szabad.
  4. Tegyük fel, hogy az 1. szál I/O műveletre vár. Ekkor kiadja a GIL-t, és a thread-2 megszerzi azt.
  5. Az I/O műveletek befejezése után, ha az 1-es szál most végre akar hajtani, ismét meg kell várnia, amíg a GIL-t felszabadítja a 2. szál.

Emiatt egyszerre csak egy szál férhet hozzá az értelmezőhöz, ami azt jelenti, hogy csak egy szál fog futni. Python kód egy adott időpontban.

Ez egymagos processzorokban rendben van, mivel időszeletelést használna (lásd a bemutató első részét) a szálak kezelésére. Többmagos processzorok esetén azonban egy CPU-hoz kötött, több szálon futó függvény jelentős hatással lesz a program hatékonyságára, mivel valójában nem fogja egyszerre az összes elérhető magot használni.

Miért volt szükség a GIL-re?

a CPython A szemétgyűjtő hatékony memóriakezelési technikát, az úgynevezett referenciaszámlálást használ. Így működik: Minden objektum a Python rendelkezik egy referenciaszámmal, amely növekszik, ha új változónévhez rendelik, vagy konténerhez adják (például tuple-khez, listákhoz stb.). Hasonlóképpen, a referenciaszám csökken, ha a hivatkozás kilép a hatókörből, vagy ha a del utasítást meghívják. Amikor egy objektum referenciaszáma eléri a nullát, a rendszer szemetet gyűjt, és a lefoglalt memória felszabadul.

A probléma azonban az, hogy a referenciaszám változója, mint bármely más globális változó, hajlamos a versenyfeltételekre. A probléma megoldása érdekében a fejlesztők Python úgy döntött, hogy a globális interpreter lockot használja. A másik lehetőség az volt, hogy minden objektumhoz hozzáadtak egy zárat, ami holtpontokat és az acquire() és release() hívások miatti megnövekedett terhelést eredményezett volna.

Ezért a GIL jelentős korlátozás a többszálú folyamatok számára. Python nagy CPU-igényű műveleteket futtató programok (ami gyakorlatilag egyszálúvá teszi őket). Ha több CPU-magot szeretne használni az alkalmazásában, használja a több feldolgozás modul helyett.

GYIK

Használjon szálkezelést I/O-igényes feladatokhoz, például hálózati hívásokhoz, fájlhozzáféréshez vagy adatbázis-lekérdezésekhez, ahol a szálak külső erőforrásokra várnak. Használjon többprocesszoros feldolgozást CPU-igényes feladatokhoz, például nehéz számításokhoz, mivel ez több magon fut és megkerüli a GIL-t.

Nem alapértelmezés szerint. A PEP 703 bevezette az opcionális szabad szálú beépítést. Python 3.13, amely letiltja a GIL-t, és Python A 3.14-es verzió folytatja. A standard buildek továbbra is tartalmazzák a GIL-t, így a legtöbb program pontosan úgy fut, mint korábban.

Egy démonszál a háttérben fut, és nem blokkolja a program kilépését. Amikor csak démonszálak maradnak, Python leállítja őket és kilép. Állítson be egyet a thread.daemon = True paranccsal a start() hívása előtt.

A Thread osztály nem adja vissza közvetlenül a run() eredményét. Tárold az értéket a thread objektumon vagy egy megosztott sorban, vagy használd a concurrent.futures.ThreadPoolExecutor metódust, melynek submit() metódusa egy Future értéket ad vissza, amit a result() függvénnyel olvasol be.

A ThreadPoolExecutor egy magas szintű segítő, amely a munkaszálak készletét kezeli. A feladatokat a submit() vagy a map() függvénnyel küldheted be, és ez kezeli a szálak létrehozását, újrafelhasználását és tisztítását, és Future objektumokat ad vissza, amelyek az egyes eredményeket tartalmazzák.

Létrehozhatsz több száz vagy több ezer szálat, de a GIL csak egy végrehajtását engedélyezi. Python bájtkód bármikor. Az I/O várakozások alatt továbbra is átfedésben vannak, így sok egyidejű szál sokkal jobban segíti az I/O-hoz kötött programokat, mint a CPU-hoz kötötteket.

Igen, főként I/O-hoz kötött lépésekhez, mint például az adatok betöltése, API-k meghívása vagy fájlok olvasása. A nehéz betanítás CPU-hoz kötött, így a NumPy és a Py könyvtárak...Torch kiadja a GIL-t C kódban, vagy a valódi párhuzamosság érdekében a többfeldolgozásra támaszkodik.

Igen. A GitHub Copilot automatikusan kiegészíti a gyakori mintákat, például a szál alosztályokat, a zárolások használatát és a ThreadPoolExecutor beállításait egy megjegyzésből vagy függvénynévből. Mindig ellenőrizze a generált kódot a versenyfeltételek és a helyes zároláskezelés szempontjából, mielőtt rá hagyatkozna.

Foglald össze ezt a bejegyzést a következőképpen: