Il modulo Threading di Python

Per programmare in multithreading con il linguaggio python, posso usare il modulo threading.

import threading

Questo modulo mi permette di lanciare ed eseguire parti del programma come sottoprocessi (thread) indipendenti.

Come creare un thread in Python

Come prima cosa, devo importare la classe Thread dal modulo threading.

from threading import Thread

Poi devo creare una classe apposita per lanciare i thread.

In questo esempio la chiamo "Lancio".

  1. class Lancio (Thread):
  2. def __init__(self, nome):
  3. Thread.__init__(self)
  4. self.nome = nome
  5. def run(self):
  6. print ("Thread '" + self.nome + "' inizio")
  7. print ("Thread '" + self.nome + "' fine")

La classe è composta dai metodi __init__ e __run__

  • __init__ contiene i parametri di inizializzazione della classe
  • __run__ contiene la procedura vera e propria da eseguire in background

In questo esempio il metodo run è composto soltanto da due istruzioni di stampa a video.

Nota. Nel metodo __init__ la classe Lancio eredita tutte le proprietà della classe Thread del modulo threading.

Una volta creata la classe di lancio, posso cominciare a lanciare i vari task.

Come eseguire più thread in parallelo

Per lanciare un thread, definisco l'oggetto (thread1) che richiama la classe Lancio con un determinato parametro ("prova 1").

thread1 = Lancio("prova 1")
thread1.start()

Poi lancio la classe con il metodo start().

Il metodo start() lancia l'esecuzione del sottoprocesso (thread) in background.

Nota. L'esecuzione del programma non si blocca con start(). Non attende la fine dell'esecuzione del task e passa immediatamente alla riga successiva.

Usando lo stesso metodo posso lanciare un secondo thread.

thread2 = Lancio("prova 2")
thread2.start()

Un terzo, un quarto, un quinto thread.

E così via.

Qual è l'output del programma?

L'output di questo semplice esempio è il seguente:

Thread 'Prova 1' inizio
Thread 'Prova 2' inizio
Thread 'Prova 1' fine
Thread 'Prova 2' fine

Come si può facilmente notare, l'esecuzione dei task di stampa non segue l'ordine sequenziale del programma principale.

Questo accade perché i thread 1 e 2 sono eseguiti in parallelo, quasi contemporaneamente, secondo i principi della programmazione multithreading.

la spiegazione degli effetti in output della programmazione multithreading

Il thread 1 comincia l'esecuzione appena prima del thread2.

Tuttavia, quando il thread2 viene lanciato il thread1 non è ancora terminato.

Per questa ragione appaiono prima entrambe le scritte "inizio" e poi quelle di "fine".

Il metodo join

Quando lancio un thread il programma "padre" continua automaticamente l'esecuzione sulla riga successiva.

Può quindi capitare che il programma "padre" termini l'esecuzione prima dei sottoprocessi ( thread ) che lancia.

Esempio

Riprendo l'esempio precedente aggiungendo un'istruzione di stampa alla fine

thread1 = Lancio("prova 1")
thread1.start()
thread2 = Lancio("prova 2")
thread2.start()
print("\n **** END **** ")

Uno degli output probabili del programma è il seguente:

Thread 'prova 1' inizio
**** END ****
Thread 'prova 2' inizio
Thread 'prova 1' fine
Thread 'prova 2' fine

In questo caso il programma "padre" termina l'esecuzione (***END***) prima ancora che il thread 2 produca i primi effetti in output.

Come sospendere l'esecuzione in attesa di un thread?

Per fare in modo che il programma aspetti la fine di un thread prima di andare avanti, posso usare il metodo join().

Va inserito dopo lo start() del thread in questione.

Esempio

Per bloccare l'esecuzione del programma finché non è finito il thread2 scrivo

thread1 = Lancio("prova 1")
thread1.start()
thread2 = Lancio("prova 2")
thread2.start()
thread2.join()
print("\n **** END **** ")

Ora l'output probabile del programma diventa il seguente:

Thread 'prova 1' inizio
Thread 'prova 2' inizio
Thread 'prova 1' fine
Thread 'prova 2' fine
**** END ****

Dopo aver lanciato il thread 2, il programma "padre" si blocca in attesa che finisca.

Quindi, l'istruzione di stampa finale (***END***) è eseguita soltanto dopo la terminazione del thread 2.

E così via.

Il metodo Lock

Per sincronizzare l'esecuzione dei thread utilizzo il metodo Lock del modulo threading.

A cosa serve?

Questo metodo include le funzioni acquire() e release()

  • acquire() permette a un thread di assumere il controllo, mettendo in pausa gli altri sottoprocessi.
  • release() permette a un thread di rilasciare il controllo, ripristinando la parallelizzazione dei sottoprocessi.

Un esempio pratico

Importo i metodi Thread e Lock dal modulo threading.

from threading import Thread, Lock

Poi definisco un oggetto con il metodo Lock che sia accessibile a tutti i thread.

Lo chiamo lucchetto.

lucchetto = Lock()

A questo punto creo la classe per lanciare i thread.

  1. class Lancio (Thread):
  2. def __init__(self, nome):
  3. Thread.__init__(self)
  4. self.nome = nome
  5. def run(self):
  6. lucchetto.acquire()
  7. print ("\n Thread '" + self.nome + "' inizio ")
  8. print ("\n Thread '" + self.nome + "' fine")
  9. lucchetto.release()

Nel metodo run inserisco le funzioni acquire() e release() rispettivamente all'inizio e alla fine del blocco di istruzioni.

Con il metodo aquire faccio assumere al thread il controllo dell'esecuzione e sospendo gli altri sottoprocessi in corso.

Al termine dell'esecuzione del thread, rilascio il controllo con la funzione release().

A questo punto lancio due thread.

thread1 = Lancio("prova 1")
thread1.start()
thread2 = Lancio("prova 2")
thread2.start()

L'output del programma è il seguente:

Thread 'prova 1' inizio
Thread 'prova 1' fine
Thread 'prova 2' inizio
Thread 'prova 2' fine

I thread sono eseguiti in batch l'uno dopo l'altro.

E' un esempio molto banale ma, secondo me, rende bene l'idea sul funzionamento della sincronizzazione dei thread in python.

E così via.

 


 

Segnalami un errore, un refuso o un suggerimento per migliorare gli appunti

FacebookTwitterLinkedinLinkedin
knowledge base
  1. Il linguaggio Python
  2. Come installare Python sul PC
  3. Come scrivere un programma in Python
  4. Come usare Python in modalità interattiva
  5. Le variabili
  6. I numeri
  7. Gli operatori logici
  8. Le strutture iterative ( o cicli )
  9. Le strutture condizionali
  10. Le eccezioni
  11. I file in python
  12. Le classi
  13. I moduli