I am implementing a multithreaded program that uses different cores, and many threads are executed simultaneously. Each thread makes a printf()
call, and the result is not readable.
How can I make printf()
atomic, so that a printf()
call in one thread doesn't conflict with a printf()
call in another?
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).
Rewritten 2017-07-26.
printf()
conceptually call flockfile()
at the start an funlockfile()
at the end, which means that the POSIX-defined stream output functions are also thread-safe per call.flockfile()
and funlockfile()
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…