2009-01-22 21 views

risposta

47

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.

+4

Il multi-threading è comune in embedded sistemi. – DanM

+22

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). –

+1

A volte è abbastanza corretto se l'output è interlacciato, ad es. durante la registrazione tramite printf da più thread. – nob

4

È 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.

+0

È 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. –

+3

Non so cosa facciano gli altri, ma la libreria GNU C è protetta da thread di default, quindi no non userà lo stesso buffer. –

+1

Non penso che 'printf' è rientranti, vedere http://stackoverflow.com/questions/3941271/why-are-malloc-and-printf-said-as-non-reentrant –

12

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.

20

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.

3

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.