Deze tutorial zal gericht zijn op een van de belangrijke onderwerpen van Python, GIL. We zullen ook bespreken hoe de GIL de prestaties van Python-programma's beïnvloedt met de code-implementatie. Voordat we op dit onderwerp ingaan, eerst een basisidee van de GIL.
GIL of Global Interpreter Lock
Python Global Interpreter Lock of GIL is een belangrijk onderdeel van multithreading-programmering. Het is een soort procesvergrendeling die wordt gebruikt bij het werken met meerdere processen. Het geeft de controle over slechts één thread. Over het algemeen gebruikt Python één enkele thread om één proces uit te voeren. We krijgen hetzelfde prestatieresultaat van de single-threaded en multi-threaded processen die de GIL gebruiken. Het beperkt het bereiken van multithreading in Python omdat het de threads voorkomt en als een enkele thread werkt.
Java-bubbel sorteren
Opmerking - Python ondersteunt geen multithreading omdat threading-pakketten ons niet in staat konden stellen de meerdere CPU-kernen te gebruiken.
Waarom Python-ontwikkelaars GIL gebruiken?
Python biedt de unieke referentietellerfunctie, die wordt gebruikt voor geheugenbeheer. De referentieteller telt het totale aantal referenties dat intern in Python is gemaakt om een waarde aan een data-object toe te kennen. Wanneer de referentietellingen nul bereiken, wordt het toegewezen geheugen van het object vrijgegeven. Laten we het onderstaande voorbeeld bekijken.
Voorbeeld -
import sys a = [] b = a sys.getrefcount(a)
Het belangrijkste probleem met de referentieaantalvariabele is dat deze kan worden beïnvloed wanneer twee of drie threads tegelijkertijd de waarde ervan proberen te verhogen of te verlagen. Het staat bekend als raceconditie. Als deze situatie zich voordoet, kan dit worden veroorzaakt door gelekt geheugen dat nooit wordt vrijgegeven. Het kan vastlopen of bugs veroorzaken in het Python-programma.
GIL helpt ons een dergelijke situatie te verwijderen door de vergrendelingen van alle gedeelde datastructuren in alle threads te gebruiken, zodat deze niet inconsistent worden gewijzigd. Python biedt een eenvoudige manier om de GIL te implementeren, omdat het zich bezighoudt met thread-safe geheugenbeheer. GIL vereist het aanbieden van een enkele vergrendeling aan een thread voor verwerking in Python. Het verhoogt de prestaties van een programma met één thread, omdat er slechts één vergrendeling hoeft te worden afgehandeld. Het helpt ook om elk CPU-gebonden programma te maken en voorkomt impasses.
De impact op Python-programma's met meerdere threads
Er is een verschil tussen CPU-grenzen in hun prestaties en I/O-gebonden voor een typisch Python-programma of een ander computerprogramma. CPU-gebonden programma's belasten de CPU over het algemeen tot het uiterste. Deze programma's worden over het algemeen gebruikt voor wiskundige berekeningen, zoals matrixvermenigvuldigingen, schroeien, beeldverwerking, enz.
I/O-gebonden programma's zijn die programma's die tijd besteden aan het verkrijgen van invoer/uitvoer die kan worden gegenereerd door de gebruiker, het bestand, de database, het netwerk, enz. Dergelijke programma's moeten een aanzienlijke hoeveelheid tijd wachten totdat de bron de invoer levert. Aan de andere kant kent de bron ook een eigen verwerkingstijd. Een gebruiker denkt bijvoorbeeld na over wat hij als invoer moet invoeren.
Laten we het volgende voorbeeld begrijpen.
int om Java te stringen
Voorbeeld -
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Uitgang:
Time taken in seconds - 7.422671556472778
Nu passen we de bovenstaande code aan door de twee threads uit te voeren.
Voorbeeld - 2:
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Uitgang:
Time taken in seconds - 6.90830135345459
Zoals we kunnen zien, duurde het even voordat beide codes klaar waren. GIL verhinderde dat de CPU-gebonden threads parallel werden uitgevoerd in de tweede code.
Waarom is de GIL nog niet verwijderd?
Veel programmeurs hebben hierover een klacht, maar Python kan de veranderingen niet zo belangrijk maken als de verwijdering van GIL. Een andere reden is dat GIL momenteel niet verbeterd is. Als het verandert in Python 3, zal dit een aantal ernstige problemen veroorzaken. In plaats van GIL te verwijderen, kan het GIL-concept verbeteren. Volgens Guido van Rossom -
'Ik zou alleen een set patches in Py3k verwelkomen als de prestaties voor een single-threaded programma (en voor een multi-threaded maar I/O-gebonden programma) niet afnemen'.
Java-verklaring
Er zijn ook veel methoden beschikbaar die hetzelfde probleem oplossen als de GIL, maar deze zijn moeilijk te implementeren.
Hoe om te gaan met Python's GIL
Het gebruik van multiprocessing is de meest geschikte manier om te voorkomen dat het programma GIL bevat. Python biedt verschillende tolken voor elk uit te voeren proces, dus in dat scenario wordt de enkele thread aan elk proces in multiprocessing verstrekt. Laten we het volgende voorbeeld begrijpen.
Voorbeeld -
from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Uitgang:
Time taken in seconds - 3.3707828521728516
Het lijkt misschien alsof de prestaties behoorlijk zijn verbeterd, maar procesbeheer heeft zijn eigen overheadkosten en meerdere processen zijn zwaarder dan meerdere threads.
Conclusie
In deze tutorial hebben we de GIL besproken en hoe we deze kunnen gebruiken. Het geeft de controle over een enkele thread die op tijd kan worden uitgevoerd. In deze tutorial werd ook besproken waarom GIL belangrijk is voor Python-programmeurs.