Come funzionano i thread in Python e quali sono le insidie ​​​​comuni specifiche del threading Python?

StackOverflow https://stackoverflow.com/questions/31340

  •  09-06-2019
  •  | 
  •  

Domanda

Ho cercato di capire come funzionano i thread in Python ed è difficile trovare buone informazioni su come funzionano.Potrei semplicemente perdere un collegamento o qualcosa del genere, ma sembra che la documentazione ufficiale non sia molto approfondita sull'argomento e non sono riuscito a trovare un buon articolo.

Da quello che posso dire, è possibile eseguire un solo thread alla volta e il thread attivo cambia ogni 10 istruzioni circa?

Dove c'è una buona spiegazione o puoi fornirne una?Sarebbe anche molto bello essere consapevoli dei problemi comuni che si incontrano durante l'utilizzo dei thread con Python.

È stato utile?

Soluzione

Sì, a causa del Global Interpreter Lock (GIL) è possibile eseguire solo un thread alla volta.Ecco alcuni link con alcuni approfondimenti a riguardo:

Dall'ultimo link una citazione interessante:

Lasciatemi spiegare cosa significa tutto ciò.I thread vengono eseguiti all'interno della stessa macchina virtuale e quindi funzionano sulla stessa macchina fisica.I processi possono funzionare sulla stessa macchina fisica o in un'altra macchina fisica.Se architetti la tua applicazione attorno ai thread, non hai fatto nulla per accedere a più macchine.Quindi, puoi ridimensionare su tutti i core sulla singola macchina (che sarà parecchi nel tempo), ma per raggiungere davvero le scale web, dovrai comunque risolvere il problema a più macchine.

Se vuoi usare multi core, pyprocessing definisce un'API basata su processi per eseguire una vera parallelizzazione.IL PEP include anche alcuni benchmark interessanti.

Altri suggerimenti

Python è un linguaggio abbastanza semplice da inserire, ma ci sono degli avvertimenti.La cosa più importante che devi sapere è il blocco globale degli interpreti.Ciò consente a un solo thread di accedere all'interprete.Ciò significa due cose:1) raramente ti ritrovi a utilizzare un'istruzione lock in Python e 2) se vuoi sfruttare i sistemi multiprocessore, devi utilizzare processi separati.MODIFICARE:Dovrei anche sottolineare che puoi inserire parte del codice in C/C++ se vuoi aggirare anche il GIL.

Pertanto, è necessario riconsiderare il motivo per cui si desidera utilizzare i thread.Se desideri parallelizzare la tua app per sfruttare l'architettura dual-core, devi considerare di suddividere l'app in più processi.

Se vuoi migliorare la reattività, dovresti CONSIDERARE l'utilizzo dei thread.Ci sono però altre alternative, vale a dire microthreading.Ci sono anche alcuni framework che dovresti esaminare:

Di seguito è riportato un esempio di threading di base.Genererà 20 thread;ogni thread mostrerà il proprio numero di thread.Eseguilo e osserva l'ordine in cui vengono stampati.

import threading
class Foo (threading.Thread):
    def __init__(self,x):
        self.__x = x
        threading.Thread.__init__(self)
    def run (self):
          print str(self.__x)

for x in xrange(20):
    Foo(x).start()

Come hai accennato, i thread Python vengono implementati tramite time-slicing.In questo modo ottengono l'effetto "parallelo".

Nel mio esempio la mia classe Foo estende il thread, quindi implemento il file run metodo, che è dove va il codice che desideri eseguire in un thread.Per avviare il thread chiami start() sull'oggetto thread, che invocherà automaticamente il file run metodo...

Naturalmente, queste sono solo le nozioni di base.Alla fine vorrai conoscere semafori, mutex e blocchi per la sincronizzazione dei thread e lo scambio di messaggi.

Utilizza i thread in Python se i singoli lavoratori stanno eseguendo operazioni legate a I/O.Se stai cercando di scalare su più core su una macchina, trova un buon IPC framework per Python o scegli una lingua diversa.

Nota: ovunque lo menzioni thread intendo specificatamente thread in Python finché non espressamente dichiarato.

I thread funzionano in modo leggermente diverso in Python se provieni C/C++ sfondo.In Python, solo un thread può essere in stato di esecuzione alla volta. Ciò significa che i thread in Python non possono veramente sfruttare la potenza di più core di elaborazione poiché per progettazione non è possibile che i thread vengano eseguiti parallelamente su più core.

Poiché la gestione della memoria in Python non è thread-safe, ogni thread richiede un accesso esclusivo alle strutture dati nell'interprete Python. Questo accesso esclusivo viene acquisito da un meccanismo chiamato GIL (blocco interprete globale).

Why does python use GIL?

Per evitare che più thread accedano simultaneamente allo stato dell'interprete e corrompano lo stato dell'interprete.

L'idea è ogni volta che viene eseguito un thread (anche se è il thread principale), viene acquisito un GIL e dopo un intervallo di tempo predefinito il GIL viene rilasciato dal thread corrente e riacquistato da qualche altro thread (se presente).

Why not simply remove GIL?

Non è impossibile rimuovere GIL, è solo che così facendo finiamo per inserire più blocchi all'interno dell'interprete per serializzare l'accesso, il che rende meno performante anche una singola applicazione con thread.

quindi il costo della rimozione di GIL viene ripagato dalla riduzione delle prestazioni di un'applicazione a thread singolo, cosa che non è mai desiderata.

So when does thread switching occurs in python?

Il cambio di thread avviene quando viene rilasciato GIL. Quindi, quando viene rilasciato GIL?Ci sono due scenari da prendere in considerazione.

Se un thread sta eseguendo operazioni vincolate alla CPU (elaborazione di immagini Ex).

Nelle versioni precedenti di Python, il cambio di thread avveniva dopo un numero fisso di istruzioni Python. Per impostazione predefinita era impostato su 100. È scoperto che non è un'ottima politica decidere quando si dovrebbe verificare il passaggio poiché il tempo trascorso a eseguire un'unica istruzione può molto selvaggiamente da millisecondi a un secondo. 100 istruzioni indipendentemente dal tempo necessario per l'esecuzione è una politica inadeguata.

Nelle nuove versioni invece di utilizzare il conteggio delle istruzioni come metrica per cambiare thread, viene utilizzato un intervallo di tempo configurabile.L'intervallo di commutazione predefinito è 5 millisecondi. È possibile ottenere l'intervallo di commutazione corrente utilizzando sys.getswitchinterval().Questo può essere modificato utilizzando sys.setswitchinterval()

Se un thread sta eseguendo alcune operazioni legate all'IO (accesso al filesystem Ex o
IO di rete)

GIL viene rilasciato ogni volta che il thread è in attesa del completamento di alcune operazioni di I/O.

Which thread to switch to next?

L’interprete non ha un proprio scheduler. Quale thread verrà schedulato alla fine dell’intervallo è la decisione del sistema operativo..

Una soluzione semplice al GIL è il multielaborazione modulo.Può essere utilizzato come sostituto del modulo threading ma utilizza più processi di interprete anziché thread.Per questo motivo c'è un po' più di sovraccarico rispetto al semplice threading per cose semplici, ma ti dà il vantaggio della parallelizzazione reale se ne hai bisogno.Inoltre, è facilmente scalabile su più macchine fisiche.

Se hai bisogno di una parallelizzazione su larga scala, guarderei oltre, ma se vuoi semplicemente scalare tutti i core di un computer o alcuni diversi senza tutto il lavoro necessario per implementare un framework più completo, allora questo è per te .

Cerca di ricordare che GIL è impostato per eseguire sondaggi di tanto in tanto per mostrare l'aspetto di più attività.Questa impostazione può essere ottimizzata, ma suggerisco che ci dovrebbe essere del lavoro svolto dai thread altrimenti molti cambi di contesto causeranno problemi.

Vorrei arrivare al punto di suggerire più genitori sui processori e provare a mantenere lavori simili sugli stessi core.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top