Qual è esattamente la funzione di Global Interpreter Lock di Python? Altri linguaggi compilati in bytecode utilizzano un meccanismo simile?
Qual è esattamente la funzione di Global Interpreter Lock di Python? Altri linguaggi compilati in bytecode utilizzano un meccanismo simile?
Risposte:
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.
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.
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
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.
reiserfs
- l'unica vera ragione che ne so).
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.
Forse questo articolo del BDFL aiuterà.