Python Threading: An Introduction – toadmin.dk

I denne tutorial lærer du, hvordan du bruger Pythons indbyggede threading-modul til at udforske multithreading-funktioner i Python.

Startende med det grundlæggende i processer og tråde, vil du lære, hvordan multithreading fungerer i Python – mens du forstår begreberne samtidighed og parallelisme. Du vil derefter lære, hvordan du starter og kører en eller flere tråde i Python ved hjælp af det indbyggede threading-modul.

Lad os komme igang.

Processer vs. tråde: Hvad er forskellene?

Hvad er en proces?

En proces er enhver forekomst af et program, der skal køre.

Det kan være hvad som helst – et Python-script eller en webbrowser såsom Chrome til en videokonferenceapplikation. Hvis du starter Task Manager på din maskine og navigerer til Ydeevne –> CPU, vil du kunne se de processer og tråde, der i øjeblikket kører på dine CPU-kerner.

Forståelse af processer og tråde

Internt har en proces en dedikeret hukommelse, der gemmer koden og dataene, der svarer til processen.

En proces består af en eller flere tråde. En tråd er den mindste sekvens af instruktioner, som operativsystemet kan udføre, og den repræsenterer udførelsesstrømmen.

Hver tråd har sin egen stak og registre, men ikke en dedikeret hukommelse. Alle tråde forbundet med en proces kan få adgang til dataene. Derfor deles data og hukommelse af alle trådene i en proces.

I en CPU med N kerner kan N processer udføres parallelt på samme tidspunkt. To tråde i den samme proces kan dog aldrig udføres parallelt – men kan udføres samtidigt. Vi vil behandle begrebet samtidighed vs. parallelisme i næste afsnit.

Baseret på hvad vi har lært indtil nu, lad os opsummere forskellene mellem en proces og en tråd.

FeatureProcessThreadMemoryDedikeret hukommelseDelt hukommelse UdførelsesmådeParallel, concurrentConcurrent; men ikke parallelExecution håndteres af Operating SystemCPython Interpreter

Multithreading i Python

I Python sikrer Global Interpreter Lock (GIL), at kun én tråd kan erhverve låsen og køre på et hvilket som helst tidspunkt. Alle tråde bør erhverve denne lås for at køre. Dette sikrer, at kun en enkelt tråd kan udføres – på ethvert givet tidspunkt – og undgår samtidig multithreading.

  Højtydende og brugervenlig Truly Global Cloud Platform

Overvej for eksempel to tråde, t1 og t2, af samme proces. Fordi tråde deler de samme data, når t1 læser en bestemt værdi k, kan t2 ændre den samme værdi k. Dette kan føre til dødvande og uønskede resultater. Men kun én af trådene kan erhverve låsen og køre på et hvilket som helst tidspunkt. Derfor sikrer GIL også gevindsikkerhed.

Så hvordan opnår vi multithreading-kapaciteter i Python? For at forstå dette, lad os diskutere begreberne samtidighed og parallelisme.

Samtidighed vs. Parallelisme: Et overblik

Overvej en CPU med mere end én kerne. I illustrationen nedenfor har CPU’en fire kerner. Det betyder, at vi kan have fire forskellige operationer kørende parallelt på ethvert givet tidspunkt.

Hvis der er fire processer, så kan hver af processerne køre uafhængigt og samtidigt på hver af de fire kerner. Lad os antage, at hver proces har to tråde.

For at forstå, hvordan threading fungerer, lad os skifte fra multicore til single-core processorarkitektur. Som nævnt kan kun en enkelt tråd være aktiv ved en bestemt eksekveringsinstans; men processorkernen kan skifte mellem trådene.

For eksempel venter I/O-bundne tråde ofte på I/O-operationer: indlæsning af brugerinput, databaselæsninger og filhandlinger. I denne ventetid kan den udløse låsen, så den anden tråd kan køre. Ventetiden kan også være en simpel operation såsom at sove i n sekunder.

Sammenfattende: Under venteoperationer udløser tråden låsen, hvilket gør det muligt for processorkernen at skifte til en anden tråd. Den tidligere tråd genoptager eksekveringen, efter at venteperioden er afsluttet. Denne proces, hvor processorkernen skifter mellem trådene samtidigt, letter multithreading. ✅

Hvis du ønsker at implementere parallelitet på procesniveau i din applikation, så overvej at bruge multiprocessing i stedet.

Python Threading Module: Første trin

Python leveres med et trådningsmodul, som du kan importere til Python-scriptet.

import threading

For at oprette et trådobjekt i Python kan du bruge trådkonstruktøren: threading.Thread(…). Dette er den generiske syntaks, der er tilstrækkelig til de fleste threading-implementeringer:

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

Her,

  • target er nøgleordsargumentet, der angiver en Python-kaldbar
  • args er den tuple af argumenter, som målet tager ind.
  Hvordan betjener man kunder i en verden af ​​social distancering?

Du skal bruge Python 3.x for at køre kodeeksemplerne i denne øvelse. Download koden og følg med.

Sådan defineres og køres tråde i Python

Lad os definere en tråd, der kører en målfunktion.

Målfunktionen er 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())

Lad os analysere, hvad ovenstående kodestykke gør:

  • Det importerer gevind- og tidsmodulerne.
  • Funktionen some_func har beskrivende print()-sætninger og inkluderer en dvaleoperation i to sekunder: time.sleep(n) får funktionen til at dvale i n sekunder.
  • Dernæst definerer vi en tråd thread_1 med målet som some_func. threading.Thread(target=…) opretter et trådobjekt.
  • Bemærk: Angiv navnet på funktionen og ikke et funktionskald; brug some_func og ikke some_func().
  • Oprettelse af et trådobjekt starter ikke en tråd; kalder start()-metoden på trådobjektet.
  • For at få antallet af aktive tråde bruger vi funktionen active_count() .

Python-scriptet kører på hovedtråden, og vi opretter en anden tråd (thread1) for at køre funktionen some_func, så det aktive trådantal er to, som det ses i outputtet:

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

Hvis vi ser nærmere på outputtet, ser vi, at når tråd1 startes, kører den første print-sætning. Men under dvaledriften skifter processoren til hovedtråden og udskriver antallet af aktive tråde – uden at vente på, at tråd1 afslutter eksekveringen.

Venter på, at tråde afsluttes med udførelse

Hvis du vil have tråd1 til at afslutte udførelsen, kan du kalde join()-metoden på den efter at have startet tråden. Hvis du gør det, venter du på, at tråd1 afslutter udførelsen uden at skifte til hovedtråden.

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 er tråd1 færdig med at udføre, før vi udskriver det aktive trådantal. Så kun hovedtråden kører, hvilket betyder, at antallet af aktive tråde er én. ✅

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

Sådan kører du flere tråde i Python

Lad os derefter oprette to tråde for at køre to forskellige funktioner.

Her er count_down en funktion, der tager et tal ind som argument og tæller ned fra dette tal til nul.

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

Vi definerer count_up, en anden Python-funktion, der tæller fra nul op til et givet tal.

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

📑 Når du bruger range()-funktionen med syntaksområdet (start, stop, step), er slutpunktets stop udelukket som standard.

  Indstil din egen genvejstast til at flytte mellem faner i Firefox

– For at tælle ned fra et bestemt tal til nul, kan du bruge en negativ trinværdi på -1 og sætte stopværdien til -1, så nul er inkluderet.

– På samme måde, for at tælle op til n, skal du indstille stopværdien til n + 1. Fordi standardværdierne for start og trin er henholdsvis 0 og 1, kan du bruge range(n + 1) for at få sekvensen 0 gennem n.

Dernæst definerer vi to tråde, tråd1 og tråd2 for at køre funktionerne count_down og count_up, henholdsvis. Vi tilføjer printudsagn og dvalefunktioner for begge funktioner.

Når du opretter trådobjekterne, skal du bemærke, at argumenterne til målfunktionen skal angives som en tuple – til parameteren args. Da begge funktioner (count_down og count_up) tager et argument ind. Du bliver nødt til at indsætte et komma eksplicit efter værdien. Dette sikrer, at argumentet stadig sendes ind som en tupel, da de efterfølgende elementer udledes som Ingen.

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

I outputtet:

  • Funktionen count_up kører på tråd2 og tæller op til 5 startende ved 0.
  • Nedtællingsfunktionen kører på tråd1 tæller ned fra 10 til 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

Du kan se, at tråd1 og tråd2 udføres alternativt, da de begge involverer en venteoperation (søvn). Når count_up-funktionen er færdig med at tælle op til 5, er tråd2 ikke længere aktiv. Så vi får output svarende til kun tråd1.

Opsummering

I denne vejledning har du lært, hvordan du bruger Pythons indbyggede threading-modul til at implementere multithreading. Her er en oversigt over de vigtigste takeaways:

  • Trådkonstruktøren kan bruges til at oprette et trådobjekt. Ved at bruge threading.Thread(target=,args=()) oprettes en tråd, der kører målet callable med argumenter specificeret i args.
  • Python-programmet kører på en hovedtråd, så de trådobjekter, du opretter, er yderligere tråde. Du kan kalde active_count()-funktionen returnerer antallet af aktive tråde i enhver instans.
  • Du kan starte en tråd ved hjælp af start()-metoden på trådobjektet og vente, indtil den afslutter eksekveringen ved hjælp af join()-metoden.

Du kan kode yderligere eksempler ved at justere ventetiderne, prøve en anden I/O-operation og mere. Sørg for at implementere multithreading i dine kommende Python-projekter. God kodning!🎉