How to use printf() in multiple threads
POSIX Specifications
The POSIX specification includes these functions:
getc_unlocked()
getchar_unlocked()
putc_unlocked()
putchar_unlock()
Versions of the functions
getc()
,getchar()
,putc()
, andputchar()
respectively namedgetc_unlocked()
,getchar_unlocked()
,putc_unlocked()
, andputchar_unlocked()
shall be provided which are functionally equivalent to the original versions, with the exception that they are not required to be implemented in a fully thread-safe manner. They shall be thread-safe when used within a scope protected byflockfile()
(orftrylockfile()
) andfunlockfile()
. These functions can safely be used in a multi-threaded program if and only if they are called while the invoking thread owns the (FILE *
) object, as is the case after a successful call to theflockfile()
orftrylockfile()
functions.
The specification for these functions mention:
flockfile()
ftrylockfile()
funlockfile()
The specification for flockfile()
et al includes the blanket requirement:
All functions that reference (
FILE *
) objects, except those with names ending in_unlocked
, shall behave as if they useflockfile()
andfunlockfile()
internally to obtain ownership of these (FILE *
) objects.
This supersedes the suggested code in previous editions of this answer. The POSIX standard also specifies:
The [
*lockfile()
] functions shall behave as if there is a lock count associated with each (FILE *
) object. This count is implicitly initialized to zero when the (FILE *
) object is created. The (FILE *
) object is unlocked when the count is zero. When the count is positive, a single thread owns the (FILE *
) object. When theflockfile()
function is called, if the count is zero or if the count is positive and the caller owns the (FILE *
) object, the count shall be incremented. Otherwise, the calling thread shall be suspended, waiting for the count to return to zero. Each call tofunlockfile()
shall decrement the count. This allows matching calls toflockfile()
(or successful calls toftrylockfile()
) andfunlockfile()
to be nested.
There are also the specifications for the character I/O functions:
The formatted output functions are documented here:
One key provision in the printf()
specification is:
Characters generated by
fprintf()
andprintf()
are printed as iffputc()
had been called.
Note the use of 'as if'. However, each of the printf()
functions is required to apply the lock so that access to a stream is controlled in a multi-threaded application. Only one thread at a time can be using a given file stream. If the operations are user-level calls to fputc()
, then other threads can intersperse the output. If the operations are user-level calls such as printf()
, then the whole call and all access to the file stream is effectively protected so that only one thread is using it until the call to printf()
returns.
In the section of the System Interfaces: General Information section of POSIX on the subject of Threads, it says:
2.9.1 Thread-Safety
All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the following functions1 need not be thread-safe.
…a list of functions that need not be thread-safe…
… The
getc_unlocked()
,getchar_unlocked()
,putc_unlocked()
, andputchar_unlocked()
functions need not be thread-safe unless the invoking thread owns the (FILE *
) object accessed by the call, as is the case after a successful call to theflockfile()
orftrylockfile()
functions.Implementations shall provide internal synchronization as necessary in order to satisfy this requirement.
The list of exempted functions does not contain fputc
or putc
or putchar
(or printf()
et al).
Interpretation
Rewritten 2017-07-26.
- Character-level output on a stream is thread-safe unless using the 'unlocked' functions without first locking the file.
- Higher-level functions such as
printf()
conceptually callflockfile()
at the start anfunlockfile()
at the end, which means that the POSIX-defined stream output functions are also thread-safe per call. - If you wish to group operations on a file stream for a single thread, you can do so by explicitly using calls to
flockfile()
andfunlockfile()
on the relevant stream (without interfering with the system's use of the*lockfile()
functions.
This means there is no need to create mutexes or equivalent mechanisms for yourself; the implementation provides the functions to allow you to control the access to printf()
et al in a multi-threaded application.
…Code from previous answer removed as no longer relevant…
In order not to mix the outputs from different threads, you need to make sure that only one thread uses printf
at a time. To achieve this, the simplest solution is to use a mutex
. At the beginning initialize the mutex
:
static pthread_mutex_t printf_mutex;...int main(){ ... pthread_mutex_init(&printf_mutex, NULL); ...
Then make a wrapper around printf
to make sure that only the thread that got the mutex
can call printf
(otherwise it will have to block until the mutex
is available) :
int sync_printf(const char *format, ...){ va_list args; va_start(args, format); pthread_mutex_lock(&printf_mutex); vprintf(format, args); pthread_mutex_unlock(&printf_mutex); va_end(args);}
For linux ,here's the code for u in c:3 threads ,executing on different cores printing hello world ,not conflicting with each other courtisey of lock .
#include <stdio.h>#include <signal.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <syscall.h>#include <sys/types.h>void * printA ( void *);void * printB ( void *);void * printC ( void *);pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;int main(int argc, char *argv[]) { int error; pthread_t tid1, tid2,tid3; if ( error = pthread_create (&tid1, NULL, printA, NULL )) { fprintf (stderr, "Failed to create first thread: %s\n",strerror(error)); return 1; } if ( error = pthread_create (&tid2, NULL, printB, NULL )) { fprintf (stderr, "Failed to create second thread: %s\n",strerror(error)); return 1; } if ( error = pthread_create (&tid3, NULL, printC, NULL )) { fprintf (stderr, "Failed to create second thread: %s\n",strerror(error)); return 1; } if (error = pthread_join(tid1, NULL)) { fprintf (stderr, "Failed to join first thread: %s\n",strerror(error)); return 1; } if (error = pthread_join(tid2, NULL)) { fprintf (stderr, "Failed to join second thread: %s\n",strerror(error)); return 1; } if (error = pthread_join(tid3, NULL)) { fprintf (stderr, "Failed to join second thread: %s\n",strerror(error)); return 1; } return 0;}void * printA ( void *arg ){ if ( error = pthread_mutex_lock( &mylock )) { fprintf (stderr, "Failed to acquire lock in printA: %s\n",strerror(error)); return NULL; } printf("Hello world\n"); if ( error = pthread_mutex_unlock( &mylock )) { fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error)); return NULL; } }void * printB ( void *arg ){ int error; if ( error = pthread_mutex_lock( &mylock )) { fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error)); return NULL; } printf("Hello world\n"); if ( error = pthread_mutex_unlock( &mylock )) { fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error)); return NULL; } }void * printC ( void *arg ){ int error; if ( error = pthread_mutex_lock( &mylock )) { fprintf (stderr, "Failed to acquire lock in printB: %s\n",strerror(error)); return NULL; } printf("Hello world\n"); if ( error = pthread_mutex_unlock( &mylock )) { fprintf (stderr, "Failed to release lock in printA: %s\n",strerror(error)); return NULL; } }