Non c'è una risposta ;-) felice di questo. Non c'è nulla di garantito in tutto ciò, che è possibile confermare semplicemente osservando che il manuale di riferimento di Python non garantisce l'atomicità.
In CPython è una questione di pragmatica. Come dice una parte tagliata dell'articolo di effbot,
In teoria, ciò significa che una contabilità esatta richiede una comprensione esatta dell'implementazione bytecode di PVM [Python Virtual Machine].
E questa è la verità. Un esperto sa CPython L += [x]
è atomica perché sanno tutti i seguenti:
+=
compila a un INPLACE_ADD
bytecode.
- L'implementazione di
INPLACE_ADD
per gli oggetti elenco è interamente scritta in C (nessun codice Python si trova nel percorso di esecuzione, pertanto GIL non può essere rilasciato tra i byte).
- In
listobject.c
, l'implementazione di INPLACE_ADD
è la funzione list_inplace_concat()
, e nulla durante la sua esecuzione deve eseguire qualsiasi codice Python utente (se così fosse, la GIL potrebbe essere nuovamente rilasciata).
Questo può sembrare incredibilmente difficile da mantenere dritto, ma per qualcuno con la conoscenza di effbot degli interni di CPython (al momento in cui ha scritto quell'articolo), in realtà non lo è. In realtà, dal momento che la profondità della conoscenza, è tutto abbastanza ovvio ;-)
Così come una questione di pragmatica, gli esperti CPython hanno sempre fatto affidamento su liberamente che "le operazioni che 'guardare atomica' in realtà dovrebbe essere atomica" e che ha anche guidato alcune decisioni linguistiche. Ad esempio, un'operazione manca dalla lista di effbot (aggiunto alla lingua dopo aver scritto quell'articolo):
x = D.pop(y) # or ...
x = D.pop(y, default)
Un argomento (al momento) a favore di aggiungere dict.pop()
era proprio che l'attuazione ovvia C sarebbe atomica , mentre l'a-uso (al momento) alternativa:
x = D[y]
del D[y]
non era atomica (il recupero e la cancellazione sono effettuate tramite bytecodes distinti, in modo thread possono passare tra di loro).
Ma i documenti non hanno mai detto.pop()
era atomico, e mai lo farà. Questo è un genere di "adulti consenzienti": se sei esperto abbastanza da sfruttarlo consapevolmente, non hai bisogno di tenere le mani. Se non sei esperto abbastanza, si applica l'ultima frase dell'articolo di effbot:
In caso di dubbio, utilizzare un mutex!
Di necessità pragmatica, sviluppatori principali sarà mai rompere l'atomicità esempi di effbot (o di D.pop()
o D.setdefault()
) in CPython. Altre implementazioni non sono affatto obbligate a imitare queste scelte pragmatiche, però. Infatti, poiché l'atomicità in questi casi si basa sulla forma specifica di bytecode di CPython combinata con l'uso di CPython di un blocco dell'interprete globale che può essere rilasciato solo tra bytecode, è essere un vero dolore per altre implementazioni per imitarli.
E non si sa mai: alcune versioni future di CPython possono rimuovere anche GIL! Ne dubito, ma è teoricamente possibile. Ma se ciò accade, scommetto che verrà mantenuta anche una versione parallela che mantiene il GIL, perché un sacco di codice (specialmente i moduli di estensione scritti in C
) si appoggia anche a GIL per la sicurezza dei thread.
Vale la pena ripetere:
In caso di dubbio, utilizzare un mutex!
È davvero sorprendente per me, non mi aspetto che succeda così. Spero che qualcuno fornisca una chiara spiegazione di ciò. –
Anche se ho messo a monte questo, penso che l'affermazione "Quali operazioni in Python sono garantite per essere thread-safe e quali no?" condanna la domanda di chiusura generale. Potresti riformularlo? – Bathsheba
In attesa di alcuni vincoli aggiunti alla domanda, ho trovato di nuovo l'effBot: [Quali tipi di mutazione del valore globale sono thread-safe?] (Http://effbot.org/pyfaq/what-kinds-of-global-value-mutation -are-thread-safe.htm) una lettura interessante.Suggerisco di riformulare in: "Quali tipi di mutazione del valore globale sono thread-safe" per essere un bel rischio ;-) Per quanto riguarda l'esempio della lista: L'elenco è thread-safe nelle sue operazioni, ma i dati stessi non sono "sicuri" dal contenitore. Quindi qualsiasi accesso alla lista che cambia il contenuto dell'elemento subirà l'intero '+ = 1'. – Dilettant