I den här guiden kommer vi att undersöka hur du kan utnyttja Pythons inbyggda modul för trådar, för att utforska möjligheterna med trådhantering i Python.
Vi börjar med en genomgång av grunderna kring processer och trådar, för att sedan utforska hur trådning fungerar i Python. Du kommer att få en djupare förståelse för begreppen samtidighet och parallellism. Därefter går vi igenom hur du initierar och kör en eller flera trådar med hjälp av den medföljande trådmodulen.
Låt oss dyka in!
Processer kontra Trådar: Vad är skillnaderna?
Vad är en process?
En process kan ses som en instans av ett program som behöver köras.
Det kan vara allt från ett enkelt Python-skript, till en webbläsare som Chrome eller en videokonferensapplikation. Om du öppnar Aktivitetshanteraren på din dator och navigerar till Prestanda -> CPU, kommer du att kunna se de processer och trådar som för närvarande körs på dina CPU-kärnor.
Förstå Processer och Trådar
Internt har varje process ett dedikerat minnesutrymme som lagrar den kod och data som tillhör just den processen.
En process består av en eller flera trådar. En tråd är den minsta enheten av instruktioner som ett operativsystem kan köra, och den representerar flödet av exekvering.
Varje tråd har sin egen stack och register, men delar processens dedikerade minne. Alla trådar som hör till en viss process kan därmed komma åt samma data och minne.
En CPU med N kärnor kan köra N processer parallellt samtidigt. Dock kan två trådar som tillhör samma process inte köras parallellt, utan istället simultant. Vi kommer att undersöka skillnaden mellan samtidighet och parallellism mer i detalj i nästa avsnitt.
Baserat på vad vi har gått igenom hittills, låt oss sammanfatta skillnaderna mellan processer och trådar.
Funktion | Process | Tråd |
Minne | Dedikerat minne | Delat minne |
Exekveringsläge | Parallellt, samtidigt | Samtidigt; men inte parallell exekvering som hanteras av operativsystemet |
Multithreading i Python
I Python ser Global Interpreter Lock (GIL) till att endast en tråd kan få tillgång till låset och köra vid en viss tidpunkt. Alla trådar måste ha detta lås för att kunna köras. Detta säkerställer att bara en tråd kan köras åt gången, vilket förhindrar samtidig flertrådning.
Tänk dig till exempel två trådar, t1 och t2, som tillhör samma process. Eftersom trådar delar samma data, kan t2 ändra ett värde k samtidigt som t1 läser det. Detta kan leda till låsningar och oönskade resultat. GIL ser dock till att endast en av trådarna kan erhålla låset och köra i taget, vilket i sin tur säkerställer trådsäkerhet.
Så hur åstadkommer vi egentligen flertrådning i Python? För att förstå detta behöver vi diskutera begreppen samtidighet och parallellism.
Samtidighet kontra Parallellism: En översikt
Föreställ dig en CPU med mer än en kärna. I illustrationen nedan har CPU:n fyra kärnor, vilket innebär att vi kan ha fyra olika operationer som körs parallellt samtidigt.
Om vi har fyra processer kan var och en av dem köras oberoende och samtidigt på var sin kärna. Låt oss anta att varje process i sin tur har två trådar.
För att förstå hur trådning fungerar, låt oss byta från en flerkärnig till en enkelkärnig processorarkitektur. Som tidigare nämnts kan endast en tråd vara aktiv under en given exekveringstid, men processorkärnan kan växla mellan olika trådar.
Till exempel väntar ofta I/O-bundna trådar på I/O-operationer: inläsning från användare, databasförfrågningar, och filoperationer. Under dessa väntetider kan tråden släppa låset så att en annan tråd kan köras. Väntetiden kan också vara en enkel operation som att ”sova” i några sekunder.
Sammanfattningsvis: under väntetider släpper tråden låset, vilket gör att processorkärnan kan växla till en annan tråd. Den tidigare tråden återupptar sin körning när väntetiden är över. Denna process, där processorkärnan växlar mellan trådarna samtidigt, gör multithreading möjligt. ✅
Om du istället vill implementera parallellitet på processnivå i din applikation kan du överväga att använda multiprocessing.
Python Threading Module: Första stegen
Python har en inbyggd trådmodul som du enkelt kan importera i dina Python-skript.
import threading
För att skapa ett trådobjekt i Python använder du trådkonstruktorn: threading.Thread(...)
. Detta är den generella syntaxen som är tillräcklig för de flesta trådimplementationer:
threading.Thread(target=...,args=...)
Här,
target
är det nyckelordsargument som anger en Python callable (en funktion eller metod)args
är en tuple som innehåller argument som ska skickas in till target.
Du behöver Python 3.x för att kunna köra kodexemplen i denna guide. Ladda ner koden och följ med.
Hur man Definierar och Kör Trådar i Python
Låt oss definiera en tråd som kör en specifik funktion.
Målfunktionen är `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())
Låt oss analysera vad kodavsnittet gör:
- Den importerar tråd- och tidsmodulerna.
- Funktionen
some_func
innehåller beskrivandeprint()
-satser och ensleep
-operation som pausar tråden i två sekunder:time.sleep(n)
gör att funktionen vilar i n sekunder. - Därefter definierar vi en tråd
thread_1
medsome_func
som mål.threading.Thread(target=...)
skapar ett trådobjekt. - Obs: Använd namnet på funktionen, inte ett funktionsanrop. Använd
some_func
istället försome_func()
. - Att skapa ett trådobjekt startar inte själva tråden. För att göra det, anropar du metoden
start()
på trådobjektet. - För att få antalet aktiva trådar använder vi funktionen
active_count()
.
Python-skriptet körs i huvudtråden, och vi skapar en till tråd (thread1) för att köra funktionen some_func
, vilket resulterar i att antalet aktiva trådar är två, vilket framgår av utskriften:
# Utskrift Running some_func... 2 Finished running some_func.
Om vi tittar närmare på utskriften ser vi att när tråd1 startas körs den första utskriftssatsen. Men under viloläget växlar processorn till huvudtråden och skriver ut antalet aktiva trådar – utan att vänta på att tråd1 ska slutföras.
Vänta på att Trådar Ska Avsluta sin Exekvering
Om du vill att thread1 ska slutföra sin exekvering, kan du anropa metoden join()
på den efter att tråden har startats. Då väntar du på att thread1 ska slutföras innan huvudtråden fortsätter.
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 har thread1
körts klart innan vi skriver ut antalet aktiva trådar. Nu körs endast huvudtråden, vilket betyder att antalet aktiva trådar är ett. ✅
# Utskrift Running some_func... Finished running some_func. 1
Hur Man Kör Flera Trådar i Python
Låt oss nu skapa två trådar för att köra två olika funktioner.
Här är count_down
en funktion som tar in ett tal som argument och räknar ner från det talet till noll.
def count_down(n): for i in range(n,-1,-1): print(i)
Vi definierar även count_up
, en annan Python-funktion som räknar från noll upp till ett givet tal.
def count_up(n): for i in range(n+1): print(i)
📑 När du använder funktionen range()
med syntaxen range(start, stop, steg)
, exkluderas slutpunkten som standard.
- För att räkna ner från ett specifikt tal till noll kan du använda ett negativt stegvärde på -1 och sätta stoppvärdet till -1 så att noll inkluderas.
- På samma sätt, för att räkna upp till n, behöver du sätta stoppvärdet till
n+1
. Eftersom standardvärdena för start och steg är 0 respektive 1 kan du användarange(n+1)
för att få sekvensen från 0 till och med n.
Därefter definierar vi två trådar, thread1
och thread2
för att köra funktionerna count_down
respektive count_up
. Vi lägger även till utskrifter och vilolägen för båda funktionerna.
När du skapar trådobjekten, notera att argumenten som skickas till målfunktionen ska anges som en tupel i parametern args
. Eftersom både count_down
och count_up
tar ett argument behöver du lägga till ett kommatecken efter värdet. Detta säkerställer att argumentet fortfarande tolkas som en tupel, eftersom efterföljande element antas vara None
.
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 utskriften:
- Funktionen
count_up
körs ithread2
och räknar upp till 5 från 0. - Funktionen
count_down
körs ithread1
och räknar ner från 10 till 0.
# Utskrift 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 ser att thread1
och thread2
körs omväxlande, eftersom båda innehåller en väntoperation (sleep
). När count_up
har räknat upp till 5, är tråd 2 inte längre aktiv. Därmed ser vi endast utskrifter från tråd 1.
Sammanfattning
I den här handledningen har du lärt dig hur du använder Pythons inbyggda trådmodul för att implementera multithreading. Här är en sammanfattning av de viktigaste punkterna:
- Trådkonstruktorn kan användas för att skapa ett trådobjekt. Genom att använda
threading.Thread(target=<callable>, args=(<tuple of args>))
skapas en tråd som kör den givna funktionen eller metoden med argumenten som anges iargs
. - Python-programmet körs i en huvudtråd. Trådobjekten du skapar är alltså ytterligare trådar. Funktionen
active_count()
returnerar antalet aktiva trådar vid ett givet tillfälle. - Du kan starta en tråd med metoden
start()
på trådobjektet och vänta tills den är klar med sin exekvering genom att använda metodenjoin()
.
Du kan experimentera vidare genom att justera väntetiderna, testa andra I/O-operationer och mer. Kom ihåg att implementera multithreading i dina kommande Python-projekt. Lycka till med kodningen!🎉