Sta scrivendo su stdout usando printf
thread-safe su Linux? Che ne pensi di utilizzare il comando di livello inferiore write
?stdout thread-safe in C su Linux?
risposta
Non è specificato dallo standard C, dipende dall'implementazione della libreria standard C. In effetti, lo standard C non menziona nemmeno i thread, poiché alcuni sistemi (ad esempio i sistemi embedded) non hanno il multithreading.
Nell'implementazione GNU (glibc
), la maggior parte delle funzioni di livello superiore in stdio che gestiscono gli oggetti FILE*
sono thread-safe. Quelle che di solito non hanno unlocked
nel loro nome (ad esempio getc_unlocked(3)
). Tuttavia, la sicurezza del thread è a livello di chiamata per funzione: se si effettuano più chiamate a printf(3)
, ad esempio, ciascuna di tali chiamate è garantita per l'output atomico, ma altri thread potrebbero stampare elementi tra le chiamate su printf()
. Se si desidera garantire che una sequenza di chiamate I/O venga emessa atomicamente, è possibile circondarle con un paio di chiamate flockfile(3)/funlockfile(3)
per bloccare l'handle FILE
. Si noti che queste funzioni sono rientranti, quindi è possibile chiamare in modo sicuro lo printf()
tra di esse e che non risulterà in un deadlock anche se si pensa che lo printf()
stesso effettui una chiamata a flockfile()
.
Le chiamate I/O di basso livello come write(2)
devono essere thread-safe, ma non ne sono sicuro al 100% - write()
effettua una chiamata di sistema nel kernel per eseguire I/O. Come esattamente ciò accade dipende dal kernel che stai usando. Potrebbe essere l'istruzione sysenter
o l'istruzione int
(interrupt) su sistemi precedenti. Una volta all'interno del kernel, è compito del kernel assicurarsi che l'I/O sia sicuro per i thread. In un test che ho appena eseguito con il Darwin Kernel versione 8.11.1, write(2)
sembra essere thread-safe.
È thread-safe; printf dovrebbe essere rientranti e non causerai alcuna stranezza o corruzione nel tuo programma.
Non è possibile garantire che l'output da un thread non inizi a metà dell'output da un altro thread. Se ti interessa, devi sviluppare il tuo codice di uscita bloccato per impedire l'accesso multiplo.
È probabile che tutte le chiamate a printf utilizzino lo stesso buffer per creare la stringa. Molte implementazioni condividono anche un buffer tra scanf e printf, che possono causare alcuni bug di debugging dipendenti. –
Non so cosa facciano gli altri, ma la libreria GNU C è protetta da thread di default, quindi no non userà lo stesso buffer. –
Non penso che 'printf' è rientranti, vedere http://stackoverflow.com/questions/3941271/why-are-malloc-and-printf-said-as-non-reentrant –
Sono entrambi thread-safe al punto che l'applicazione non si bloccherà se più thread li chiamano sullo stesso descrittore di file. Tuttavia, senza alcun blocco a livello di applicazione, tutto ciò che è scritto potrebbe essere interfogliato.
Se lo si chiami "thread-safe" dipende dalla definizione di thread-safe. POSIX richiede le funzioni stdio
per utilizzare il blocco, quindi il programma non si blocca, corregge gli stati dell'oggetto FILE
, ecc. Se si utilizza printf
contemporaneamente da più thread.
Tuttavia, tutte le operazioni
stdio
vengono formalmente specificate in termini di chiamate ripetute a
fgetc
e
fputc
, pertanto non è garantita un'atomicità su più larga scala. Vale a dire, se i thread 1 e 2 provano a stampare
"Hello\n"
e
"Goodbye\n"
allo stesso tempo, non è possibile garantire che l'output sia
"Hello\nGoodbye\n"
o
"Goodbye\nHello\n"
. Potrebbe anche essere
"HGelolodboy\ne\n"
. In pratica, la maggior parte delle implementazioni acquisirà un singolo blocco per l'intera chiamata di scrittura di livello superiore semplicemente perché è più efficiente, ma il tuo programma non dovrebbe assumerlo. Ci possono essere casi d'angolo in cui ciò non viene fatto; ad esempio un'implementazione potrebbe probabilmente omettere il blocco su stream non bufferizzati.
Modifica: Il testo sopra di atomicità non è corretto. POSIX garantisce tutti stdio
operazioni sono atomiche, ma la garanzia è nascosto nella documentazione per flockfile
: http://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html
Tutte le funzioni che fanno riferimento (FILE *) gli oggetti si comportano come se utilizzano flockfile() e funlockfile() internamente ottenere la proprietà di questi oggetti (FILE *).
È possibile utilizzare i flockfile
, ftrylockfile
, e funlockfile
funzioni se stessi per raggiungere le scritture atomiche larger-than-a funzione singola chiamata.
C ha ottenuto un nuovo standard da quando è stata posta questa domanda (e ultima risposta).
C11 viene ora con supporto multithreading e indirizzi comportamento multithreading di flussi:
§7.21.2 Streams
¶7 Ogni flusso ha una serratura associata utilizzata per impedire le corse di dati quando più thread di esecuzione accedono a un flusso e a limitare l'interleaving delle operazioni di streaming eseguite da più thread. Solo una discussione può contenere questo blocco alla volta. Il blocco è rientrante: un singolo thread può trattenere il blocco più volte in un dato momento.
¶8 Tutte le funzioni che leggono, scrivono, posizionano o eseguono query sulla posizione di un flusso bloccano il flusso prima di accedervi. Rilasciano il blocco associato allo stream quando l'accesso è completo.
Quindi, un'implementazione con thread C11 deve garantire che l'utilizzo di printf
è thread-safe.
Sia atomicità (come in nessun interleaving 1) è garantito, non era chiaro per me a prima vista, perché lo standard ha parlato di limitare interleaving, al contrario di prevenire, quale mandato per le corse di dati.
Mi chino verso di esso è garantito. Lo standard parla di limitando l'interleaving, poiché è ancora possibile che si verifichi un interleaving che non modifica l'esito; ad es.fwrite
alcuni byte, fseek
indietro ancora alcuni e fwrite
fino allo scostamento originale, in modo che entrambi i fwrite
siano back-to-back. L'implementazione è gratuita per riordinare questi 2 fwrite
s e unirli in un'unica scrittura.
1: Vedere il testo strike-through in R..'s answer per un esempio.
Il multi-threading è comune in embedded sistemi. – DanM
Questa risposta sta ignorando che la domanda è stata taggata unix/linux. POSIX richiede che stdio sia sicuro per i thread, il che è piuttosto sfavorevole dato che uccide le prestazioni e poiché non esiste un modo pratico per operare sullo stesso file da più thread (i dati verranno fuori irrimediabilmente interlacciati, l'atomicità è solo a livello di carattere). –
A volte è abbastanza corretto se l'output è interlacciato, ad es. durante la registrazione tramite printf da più thread. – nob