Implementazione dei thread

In questo capitolo impareremo come implementare i thread in Python.

Modulo Python per l'implementazione dei thread

I thread Python sono talvolta chiamati processi leggeri perché i thread occupano molta meno memoria dei processi. I thread consentono di eseguire più attività contemporaneamente. In Python, abbiamo i seguenti due moduli che implementano i thread in un programma:

  • <_thread>module

  • <threading>module

La differenza principale tra questi due moduli è quella <_thread> module tratta un thread come una funzione mentre, il <threading>module tratta ogni thread come un oggetto e lo implementa in modo orientato agli oggetti. Inoltre, il<_thread>è efficace nel threading di basso livello e ha meno capacità del <threading> modulo.

modulo <_thread>

Nella versione precedente di Python, avevamo l'estensione <thread>ma è stato considerato "deprecato" per un periodo piuttosto lungo. Gli utenti sono stati incoraggiati a utilizzare il<threading>modulo invece. Pertanto, in Python 3 il modulo "thread" non è più disponibile. È stato rinominato "<_thread>"per incompatibilità all'indietro in Python3.

Per generare un nuovo thread con l'aiuto di <_thread> modulo, dobbiamo chiamare il file start_new_threadmetodo di esso. Il funzionamento di questo metodo può essere compreso con l'aiuto della seguente sintassi:

_thread.start_new_thread ( function, args[, kwargs] )

Qui -

  • args è una tupla di argomenti

  • kwargs è un dizionario opzionale di argomenti di parole chiave

Se vogliamo chiamare la funzione senza passare un argomento, allora dobbiamo usare una tupla vuota di argomenti in args.

Questa chiamata al metodo restituisce immediatamente, il thread figlio viene avviato e chiama la funzione con l'elenco passato, se presente, di argomenti. Il thread termina come e quando la funzione ritorna.

Esempio

Di seguito è riportato un esempio per la generazione di un nuovo thread utilizzando il <_thread>modulo. Stiamo usando il metodo start_new_thread () qui.

import _thread
import time

def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

try:
   _thread.start_new_thread( print_time, ("Thread-1", 2, ) )
   _thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
   print ("Error: unable to start thread")
while 1:
   pass

Produzione

Il seguente output ci aiuterà a capire la generazione di nuovi thread con l'aiuto di <_thread> modulo.

Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018

modulo <threading>

Il <threading>il modulo implementa in modo orientato agli oggetti e tratta ogni thread come un oggetto. Pertanto, fornisce un supporto molto più potente e di alto livello per i thread rispetto al modulo <_thread>. Questo modulo è incluso in Python 2.4.

Metodi aggiuntivi nel modulo <threading>

Il <threading> Il modulo comprende tutti i metodi di <_thread>ma fornisce anche metodi aggiuntivi. I metodi aggiuntivi sono i seguenti:

  • threading.activeCount() - Questo metodo restituisce il numero di oggetti thread attivi

  • threading.currentThread() - Questo metodo restituisce il numero di oggetti thread nel controllo thread del chiamante.

  • threading.enumerate() - Questo metodo restituisce un elenco di tutti gli oggetti thread attualmente attivi.

  • Per implementare il threading, il <threading> il modulo ha l'estensione Thread classe che fornisce i seguenti metodi:

    • run() - Il metodo run () è il punto di ingresso per un thread.

    • start() - Il metodo start () avvia un thread chiamando il metodo run.

    • join([time]) - Il join () attende che i thread terminino.

    • isAlive() - Il metodo isAlive () controlla se un thread è ancora in esecuzione.

    • getName() - Il metodo getName () restituisce il nome di un thread.

    • setName() - Il metodo setName () imposta il nome di un thread.

Come creare thread utilizzando il modulo <threading>?

In questa sezione impareremo come creare thread usando il <threading>modulo. Segui questi passaggi per creare un nuovo thread utilizzando il modulo <threading> -

  • Step 1 - In questo passaggio, dobbiamo definire una nuova sottoclasse di Thread classe.

  • Step 2 - Quindi per aggiungere ulteriori argomenti, dobbiamo sovrascrivere il file __init__(self [,args]) metodo.

  • Step 3 - In questo passaggio, dobbiamo sovrascrivere il metodo run (self [, args]) per implementare ciò che il thread dovrebbe fare all'avvio.

  • Ora, dopo aver creato il nuovo Thread sottoclasse, possiamo creare un'istanza di esso e quindi avviare un nuovo thread invocando il start(), che a sua volta chiama il run() metodo.

Esempio

Considera questo esempio per imparare a generare un nuovo thread usando il <threading> modulo.

import threading
import time
exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting " + self.name)
      print_time(self.name, self.counter, 5)
      print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print ("%s: %s" % (threadName, time.ctime(time.time())))
      counter -= 1

thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2

Produzione

Ora, considera il seguente output:

Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread

Programma Python per vari stati di thread

Ci sono cinque stati del thread: nuovo, eseguibile, in esecuzione, in attesa e morto. Tra questi cinque Di questi cinque, ci concentreremo principalmente su tre stati: in esecuzione, in attesa e morto. Un thread ottiene le proprie risorse nello stato di esecuzione, attende le risorse nello stato di attesa; il rilascio finale della risorsa, se in esecuzione e acquisito, è allo stato morto.

Il seguente programma Python con l'aiuto dei metodi start (), sleep () e join () mostrerà come un thread è entrato rispettivamente nello stato di esecuzione, attesa e morto.

Step 1 - Importa i moduli necessari, <threading> e <time>

import threading
import time

Step 2 - Definisci una funzione, che verrà chiamata durante la creazione di un thread.

def thread_states():
   print("Thread entered in running state")

Step 3 - Stiamo usando il metodo sleep () del modulo time per far aspettare il nostro thread diciamo 2 secondi.

time.sleep(2)

Step 4 - Ora stiamo creando un thread denominato T1, che accetta l'argomento della funzione definita sopra.

T1 = threading.Thread(target=thread_states)

Step 5- Ora, con l'aiuto della funzione start () possiamo avviare il nostro thread. Produrrà il messaggio, che è stato impostato da noi durante la definizione della funzione.

T1.start()
Thread entered in running state

Step 6 - Ora, finalmente possiamo uccidere il thread con il metodo join () dopo che ha terminato la sua esecuzione.

T1.join()

Avvio di un thread in Python

In python, possiamo iniziare un nuovo thread in modi diversi, ma il più semplice tra loro è definirlo come una singola funzione. Dopo aver definito la funzione, possiamo passarla come obiettivo per un nuovo filethreading.Threadoggetto e così via. Esegui il seguente codice Python per capire come funziona la funzione:

import threading
import time
import random
def Thread_execution(i):
   print("Execution of Thread {} started\n".format(i))
   sleepTime = random.randint(1,4)
   time.sleep(sleepTime)
   print("Execution of Thread {} finished".format(i))
for i in range(4):
   thread = threading.Thread(target=Thread_execution, args=(i,))
   thread.start()
   print("Active Threads:" , threading.enumerate())

Produzione

Execution of Thread 0 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>]

Execution of Thread 1 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>]

Execution of Thread 2 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>]

Execution of Thread 3 started
Active Threads:
   [<_MainThread(MainThread, started 6040)>,
      <HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
      <Thread(Thread-3576, started 3932)>,
      <Thread(Thread-3577, started 3080)>,
      <Thread(Thread-3578, started 2268)>,
      <Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished

Thread daemon in Python

Prima di implementare i thread daemon in Python, è necessario conoscere i thread daemon e il loro utilizzo. In termini di elaborazione, il daemon è un processo in background che gestisce le richieste di vari servizi come l'invio di dati, i trasferimenti di file, ecc. Sarebbe dormiente se non fosse più necessario. La stessa operazione può essere eseguita anche con l'aiuto di thread non daemon. Tuttavia, in questo caso, il thread principale deve tenere traccia dei thread non daemon manualmente. D'altra parte, se stiamo usando i thread daemon, il thread principale può dimenticarsene completamente e verrà ucciso quando il thread principale esce. Un altro punto importante sui thread daemon è che possiamo scegliere di usarli solo per attività non essenziali che non ci influenzerebbero se non si completano o vengono uccisi nel mezzo. Di seguito è riportata l'implementazione dei thread daemon in python:

import threading
import time

def nondaemonThread():
   print("starting my thread")
   time.sleep(8)
   print("ending my thread")
def daemonThread():
   while True:
   print("Hello")
   time.sleep(2)
if __name__ == '__main__':
   nondaemonThread = threading.Thread(target = nondaemonThread)
   daemonThread = threading.Thread(target = daemonThread)
   daemonThread.setDaemon(True)
   daemonThread.start()
   nondaemonThread.start()

Nel codice sopra, ci sono due funzioni, vale a dire >nondaemonThread() e >daemonThread(). La prima funzione stampa il suo stato e dorme dopo 8 secondi mentre la funzione deamonThread () stampa Hello dopo ogni 2 secondi indefinitamente. Possiamo capire la differenza tra nondaemon e thread daemon con l'aiuto del seguente output:

Hello

starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello