Perché Global Interpreter Lock?


89

Qual è esattamente la funzione di Global Interpreter Lock di Python? Altri linguaggi compilati in bytecode utilizzano un meccanismo simile?


6
Dovresti anche chiedere "Ha importanza?"
S.Lott

2
Sono d'accordo, lo considero un non problema ora che in 2.6 è stato aggiunto il modulo multiprocessing per consentire di programmare utilizzando più processi in modo thread-like. docs.python.org/library/multiprocessing.html
monkut

Risposte:


69

In generale, per qualsiasi problema di thread safety sarà necessario proteggere le strutture dati interne con blocchi. Questo può essere fatto con vari livelli di granularità.

  • È possibile utilizzare un blocco a grana fine, in cui ogni struttura separata ha il proprio blocco.

  • È possibile utilizzare un blocco a grana grossa in cui un blocco protegge tutto (l'approccio GIL).

Ci sono vari pro e contro di ogni metodo. Il blocco a grana fine consente un parallelismo maggiore: due thread possono essere eseguiti in parallelo quando non condividono alcuna risorsa. Tuttavia c'è un sovraccarico amministrativo molto più ampio. Per ogni riga di codice, potrebbe essere necessario acquisire e rilasciare diversi blocchi.

L'approccio a grana grossa è l'opposto. Non è possibile eseguire due thread contemporaneamente, ma un singolo thread verrà eseguito più velocemente perché non fa così tanta contabilità. Alla fine si tratta di un compromesso tra velocità a thread singolo e parallelismo.

Ci sono stati alcuni tentativi di rimuovere il GIL in python, ma l'overhead aggiuntivo per le macchine a thread singolo era generalmente troppo grande. Alcuni casi possono effettivamente essere più lenti anche su macchine multiprocessore a causa di conflitti di blocco.

Altri linguaggi compilati in bytecode utilizzano un meccanismo simile?

Varia e probabilmente non dovrebbe essere considerata una proprietà del linguaggio quanto una proprietà di implementazione. Ad esempio, ci sono implementazioni Python come Jython e IronPython che utilizzano l'approccio di threading della loro VM sottostante, piuttosto che un approccio GIL. Inoltre, la prossima versione di Ruby sembra orientarsi verso l' introduzione di un GIL.


1
puoi spiegare questo: "Non è possibile eseguire due thread contemporaneamente"? Recentemente ho scritto un semplice webserver in Python con multithreading. Per ogni nuova richiesta dal client, i server generano un nuovo thread e quei thread continuano a essere eseguiti. Quindi ci saranno più thread in esecuzione allo stesso tempo, giusto? O ho capito male?
avi

1
@avi AFAIK i thread python non possono essere eseguiti contemporaneamente, ma ciò non significa che un thread debba bloccare l'altro. GIL significa solo che un solo thread può interpretare il codice Python alla volta, non significa che la gestione dei thread e l'allocazione delle risorse non funzionino.
Benproductions1

2
^ quindi, in qualsiasi momento, solo un thread servirà il contenuto al client ... quindi non ha senso utilizzare effettivamente il multithreading per migliorare le prestazioni. giusto?
avi

E, naturalmente, Java è compilato in byte code e consente un blocco granulare molto fine.
Warren Dew

3
@avi, un processo legato a IO come un server web può ancora guadagnare dai thread Python. Due o più thread possono eseguire operazioni di I / O contemporaneamente. Non possono essere interpretati (CPU) contemporaneamente.
Saish

33

Quanto segue è tratto dal manuale di riferimento API Python / C ufficiale :

L'interprete Python non è completamente thread-safe. Per supportare programmi Python multi-thread, esiste un blocco globale che deve essere mantenuto dal thread corrente prima che possa accedere in sicurezza agli oggetti Python. Senza il blocco, anche le operazioni più semplici potrebbero causare problemi in un programma multi-thread: ad esempio, quando due thread incrementano simultaneamente il conteggio dei riferimenti dello stesso oggetto, il conteggio dei riferimenti potrebbe finire per essere incrementato solo una volta anziché due.

Pertanto, esiste la regola che solo il thread che ha acquisito il blocco dell'interprete globale può operare su oggetti Python o chiamare funzioni API Python / C. Per supportare programmi Python multi-thread, l'interprete rilascia e riacquisisce regolarmente il blocco - per impostazione predefinita, ogni 100 istruzioni bytecode (questo può essere modificato con sys.setcheckinterval ()). Il blocco viene anche rilasciato e riacquistato per operazioni di I / O potenzialmente bloccanti come la lettura o la scrittura di un file, in modo che altri thread possano essere eseguiti mentre il thread che richiede l'I / O è in attesa del completamento dell'operazione di I / O.

Penso che riassuma abbastanza bene il problema.


1
L'ho letto anch'io, ma non riesco a capire perché Python sia diverso in questo senso da, diciamo, java (vero?)
Federico A. Ramponi

@EliBendersky I thread Python sono implementati come pthread e sono gestiti dal sistema operativo ( dabeaz.com/python/UnderstandingGIL.pdf ) mentre i thread Java sono thread a livello di applicazione la cui pianificazione è gestita dalla JVM
gokul_uf

19

Il blocco dell'interprete globale è un grande blocco di tipo mutex che protegge i contatori di riferimento dall'essere hosed. Se stai scrivendo codice Python puro, tutto questo accade dietro le quinte, ma se incorpori Python in C, potresti dover prendere / rilasciare esplicitamente il blocco.

Questo meccanismo non è correlato alla compilazione di Python in bytecode. Non è necessario per Java. In effetti, non è nemmeno necessario per Jython (python compilato su jvm).

vedi anche questa domanda


4
"Questo meccanismo non è correlato alla compilazione di Python in bytecode": Precisamente, è un artefatto dell'implementazione di CPython. Altre implementazioni (come Jython che hai citato) possono essere libere da questa restrizione in virtù della loro implementazione thread-safe
Eli Bendersky

11

Python, come perl 5, non è stato progettato da zero per essere thread-safe. I thread sono stati innestati dopo il fatto, quindi il blocco dell'interprete globale viene utilizzato per mantenere l'esclusione reciproca in cui solo un thread sta eseguendo codice in un dato momento nelle viscere dell'interprete.

I singoli thread Python vengono multitasking in modo cooperativo dall'interprete stesso, ripetendo il blocco ogni tanto.

Afferrare il blocco da soli è necessario quando si parla con Python da C quando altri thread Python sono attivi per "aderire" a questo protocollo e assicurarsi che non accada nulla di pericoloso alle vostre spalle.

Altri sistemi che hanno un'eredità a thread singolo che in seguito si è evoluta in sistemi con più thread hanno spesso un meccanismo di questo tipo. Ad esempio, il kernel Linux ha il "Big Kernel Lock" dai suoi primi giorni SMP. A poco a poco nel tempo, man mano che le prestazioni multi-threading diventano un problema, c'è la tendenza a provare a suddividere questi tipi di blocchi in pezzi più piccoli o sostituirli con algoritmi senza blocchi e strutture di dati, ove possibile, per massimizzare il throughput.


+1 per aver menzionato il fatto che il blocco a grana grossa è usato di quanto la maggior parte pensi, specialmente il BKL spesso dimenticato (io uso reiserfs- l'unica vera ragione che ne so).
nuovo123456

3
Linux aveva BKL, dalla versione 2.6.39 BKL è stato completamente rimosso.
avi

5
Ovviamente. Intendiamoci, sono passati ~ 3 anni dalla risposta alla domanda. =)
Edward KMETT

7

Per quanto riguarda la tua seconda domanda, non tutti i linguaggi di scripting lo usano, ma li rende solo meno potenti. Ad esempio, i thread in Ruby sono verdi e non nativi.

In Python, i thread sono nativi e il GIL impedisce loro di funzionare solo su core diversi.

In Perl, i thread sono anche peggiori. Copiano semplicemente l'intero interprete e sono ben lungi dall'essere utilizzabili come in Python.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.