Python Threading: een inleiding – epcdream.nl

In deze zelfstudie leert u hoe u de ingebouwde threadingmodule van Python gebruikt om multithreading-mogelijkheden in Python te verkennen.

Beginnend met de basisprincipes van processen en threads, leer je hoe multithreading werkt in Python, terwijl je de concepten van gelijktijdigheid en parallellisme begrijpt. Vervolgens leer je hoe je een of meer threads in Python start en uitvoert met behulp van de ingebouwde threading-module.

Laten we beginnen.

Processen versus threads: wat zijn de verschillen?

Wat is een proces?

Een proces is elk exemplaar van een programma dat moet worden uitgevoerd.

Het kan van alles zijn: een Python-script of een webbrowser zoals Chrome tot een toepassing voor videoconferenties. Als u Taakbeheer op uw computer start en naar Prestaties -> CPU navigeert, kunt u de processen en threads zien die momenteel op uw CPU-kernen worden uitgevoerd.

Processen en threads begrijpen

Intern heeft een proces een speciaal geheugen dat de code en gegevens opslaat die bij het proces horen.

Een proces bestaat uit een of meer threads. Een thread is de kleinste reeks instructies die het besturingssysteem kan uitvoeren en vertegenwoordigt de uitvoeringsstroom.

Elke thread heeft zijn eigen stapel en registers, maar geen speciaal geheugen. Alle threads die aan een proces zijn gekoppeld, hebben toegang tot de gegevens. Daarom worden gegevens en geheugen gedeeld door alle threads van een proces.

In een CPU met N cores kunnen N processen tegelijkertijd worden uitgevoerd. Twee threads van hetzelfde proces kunnen echter nooit parallel worden uitgevoerd, maar kunnen gelijktijdig worden uitgevoerd. We zullen het concept van gelijktijdigheid versus parallellisme in de volgende sectie behandelen.

Laten we op basis van wat we tot nu toe hebben geleerd de verschillen tussen een proces en een thread samenvatten.

FeatureProcessThreadMemoryToegewezen geheugenGedeeld geheugenUitvoeringsmodusParallel, gelijktijdigConcurrent; maar niet parallelUitvoering afgehandeld door besturingssysteemCPython Interpreter

Multithreading in Python

In Python zorgt de Global Interpreter Lock (GIL) ervoor dat slechts één thread de vergrendeling kan verkrijgen en op elk moment kan uitvoeren. Alle threads moeten dit slot krijgen om te kunnen worden uitgevoerd. Dit zorgt ervoor dat er slechts één thread kan worden uitgevoerd – op een bepaald moment – en vermijdt gelijktijdig multithreading.

Beschouw bijvoorbeeld twee threads, t1 en t2, van hetzelfde proces. Omdat threads dezelfde gegevens delen wanneer t1 een bepaalde waarde k leest, kan t2 dezelfde waarde k wijzigen. Dit kan leiden tot impasses en ongewenste resultaten. Maar slechts één van de threads kan het slot verkrijgen en op elk moment worden uitgevoerd. Daarom zorgt GIL ook voor draadveiligheid.

  Ontvang een melding wanneer AirPods op voorraad zijn bij uw dichtstbijzijnde Apple Store

Dus hoe bereiken we multithreading-mogelijkheden in Python? Laten we, om dit te begrijpen, de concepten van gelijktijdigheid en parallellisme bespreken.

Gelijktijdigheid versus parallellisme: een overzicht

Overweeg een CPU met meer dan één kern. In de onderstaande afbeelding heeft de CPU vier kernen. Dit betekent dat we op elk moment vier verschillende bewerkingen parallel kunnen laten lopen.

Als er vier processen zijn, kan elk van de processen onafhankelijk en gelijktijdig op elk van de vier kernen draaien. Laten we aannemen dat elk proces twee threads heeft.

Laten we, om te begrijpen hoe threading werkt, overschakelen van multicore- naar single-coreprocessorarchitectuur. Zoals vermeld, kan slechts een enkele thread actief zijn op een bepaalde uitvoeringsinstantie; maar de processorkern kan schakelen tussen de threads.

I/O-gebonden threads wachten bijvoorbeeld vaak op I/O-bewerkingen: inlezen van gebruikersinvoer, databaselezen en bestandsbewerkingen. Tijdens deze wachttijd kan hij het slot ontgrendelen zodat de andere thread kan lopen. De wachttijd kan ook een simpele handeling zijn zoals n seconden slapen.

Samengevat: tijdens wachtbewerkingen geeft de thread de vergrendeling vrij, waardoor de processorkern naar een andere thread kan schakelen. De eerdere thread wordt hervat nadat de wachttijd is verstreken. Dit proces, waarbij de processorkern gelijktijdig tussen de threads schakelt, vergemakkelijkt multithreading.

Als u parallellisme op procesniveau in uw toepassing wilt implementeren, kunt u overwegen om in plaats daarvan multiprocessing te gebruiken.

Python Threading-module: eerste stappen

Python wordt geleverd met een threading-module die u in het Python-script kunt importeren.

import threading

Om een ​​thread-object in Python te maken, kunt u de Thread-constructor gebruiken: threading.Thread(…). Dit is de generieke syntaxis die voldoende is voor de meeste threading-implementaties:

threading.Thread(target=...,args=...)

Hier,

  • target is het sleutelwoordargument dat een Python-callable aanduidt
  • args is het tupel van argumenten dat het doelwit opneemt.

U hebt Python 3.x nodig om de codevoorbeelden in deze zelfstudie uit te voeren. Download de code en volg mee.

Threads definiëren en uitvoeren in Python

Laten we een thread definiëren die een doelfunctie uitvoert.

De doelfunctie is 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())

Laten we ontleden wat het bovenstaande codefragment doet:

  • Het importeert de threading en de tijdmodules.
  • De functie some_func heeft beschrijvende print()-instructies en bevat een slaapbewerking voor twee seconden: time.sleep(n) zorgt ervoor dat de functie n seconden slaapt.
  • Vervolgens definiëren we een thread thread_1 met het doel als some_func. threading.Thread(target=…) maakt een thread-object aan.
  • Opmerking: geef de naam van de functie op en geen functieaanroep; gebruik some_func en niet some_func().
  • Als u een thread-object maakt, wordt er geen thread gestart; het aanroepen van de methode start() op het thread-object doet dat wel.
  • Om het aantal actieve threads te krijgen, gebruiken we de active_count() functie.
  Voeg Ok Google-detectie toe aan alle schermen op uw apparaat [No Root]

Het Python-script wordt uitgevoerd op de hoofdthread en we maken een andere thread (thread1) om de functie some_func uit te voeren, dus het aantal actieve threads is twee, zoals te zien is in de uitvoer:

# Output
Running some_func...
2
Finished running some_func.

Als we de uitvoer nader bekijken, zien we dat bij het starten van thread1 het eerste printstatement wordt uitgevoerd. Maar tijdens de slaapbewerking schakelt de processor over naar de hoofdthread en drukt het aantal actieve threads af – zonder te wachten tot thread1 klaar is met uitvoeren.

Wachten op threads om uitvoering te voltooien

Als je wilt dat thread1 de uitvoering voltooit, kun je de methode join() erop aanroepen nadat je de thread hebt gestart. Als u dit doet, wordt gewacht tot thread1 is voltooid zonder over te schakelen naar de hoofdthread.

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())

Nu is thread1 klaar met uitvoeren voordat we het actieve aantal threads afdrukken. Dus alleen de hoofdthread is actief, wat betekent dat het aantal actieve threads één is.

# Output
Running some_func...
Finished running some_func.
1

Meerdere threads uitvoeren in Python

Laten we vervolgens twee threads maken om twee verschillende functies uit te voeren.

Hier is count_down een functie die een getal als argument inneemt en van dat getal naar nul aftelt.

def count_down(n):
    for i in range(n,-1,-1):
        print(i)

We definiëren count_up, een andere Python-functie die telt van nul tot een bepaald getal.

def count_up(n):
    for i in range(n+1):
        print(i)

📑 Bij gebruik van de functie range() met de syntaxis range(start, stop, step), wordt de eindpuntstop standaard uitgesloten.

– Om af te tellen van een bepaald getal naar nul, kunt u een negatieve stapwaarde van -1 gebruiken en de stopwaarde instellen op -1 zodat nul wordt meegerekend.

– Evenzo, om tot n te tellen, moet u de stopwaarde instellen op n + 1. Omdat de standaardwaarden van start en stap respectievelijk 0 en 1 zijn, kunt u bereik (n + 1) gebruiken om de reeks 0 te krijgen via nl.

Vervolgens definiëren we twee threads, thread1 en thread2 om respectievelijk de functies count_down en count_up uit te voeren. We voegen afdrukinstructies en slaapbewerkingen toe voor beide functies.

  Waar kunt u sjablonen voor juridische documenten voor uw bedrijf krijgen?

Let er bij het maken van de thread-objecten op dat de argumenten voor de doelfunctie moeten worden opgegeven als een tuple – voor de parameter args. Omdat beide functies (count_down en count_up) één argument bevatten. U moet expliciet een komma invoegen na de waarde. Dit zorgt ervoor dat het argument nog steeds als een tuple wordt doorgegeven, omdat de volgende elementen worden afgeleid als Geen.

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()

In de uitvoer:

  • De functie count_up draait op thread2 en telt tot 5 vanaf 0.
  • De count_down-functie draait op thread1 en telt af van 10 naar 0.
# 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

Je kunt zien dat thread1 en thread2 afwisselend worden uitgevoerd, omdat beide een wachtbewerking (slaap) inhouden. Zodra de count_up-functie tot 5 is geteld, is thread2 niet langer actief. Dus we krijgen de uitvoer die overeenkomt met alleen thread1.

Opsommen

In deze zelfstudie hebt u geleerd hoe u de ingebouwde threadingmodule van Python kunt gebruiken om multithreading te implementeren. Hier is een samenvatting van de belangrijkste afhaalrestaurants:

  • De Thread-constructor kan worden gebruikt om een ​​thread-object te maken. Door gebruik te maken van threading.Thread(target=,args=()) wordt een thread gemaakt die de target callable uitvoert met argumenten gespecificeerd in args.
  • Het Python-programma draait op een hoofdthread, dus de thread-objecten die u maakt, zijn extra threads. U kunt de functie active_count() aanroepen, retourneert het aantal actieve threads op elk moment.
  • U kunt een thread starten met behulp van de methode start() op het thread-object en wachten tot de uitvoering is voltooid met behulp van de methode join().

U kunt extra voorbeelden coderen door de wachttijden aan te passen, een andere I/O-bewerking uit te proberen en meer. Zorg ervoor dat u multithreading implementeert in uw aankomende Python-projecten. Veel plezier met coderen!

gerelateerde berichten