The C++ Standard says nothing about multithreaded environments; nevertheless most nontrivial operating systems support some form of threading. (See the Dinkum Threads Library for a portable to way to control multiple threads from a C or C++ program.) In such an environment, it is important to know what operations are safe on data that is shared between two or more threads, what operations are unsafe, and what you can do to make unsafe operations safe.
The Dinkum C++11 Libraries can be built either as
multithreaded or single-threaded. The former is, of course,
the most robust; but the latter can be smaller and faster
in an environment where thread safety is never an issue,
because all threading code is omitted.
The setup scripts that select the active library, and
optionally build it, have the options MT
,
to select a multithreaded library, and NO_MT
,
to select a single-threaded library. Multithreaded is the
usual default.
The following descriptions apply only to multithreaded libraries.
In the Standard C Library, all input/output is mediated
by objects of type
FILE
. Atomic
operations on these objects, such as calls to
getc
and
putc
,
are protected against simultaneous access from different threads.
Thus, you can safely share FILE
objects across
threads without worrying about loss of integrity. But you
might not like the way file accesses interleave, so you
typically need additional thread locks at the appropriate
level of granularity.
If you want changes to the file to be reflected after
each atomic write, you must change the definition of the
macro _FILE_OP_LOCKS
from 0
to 1
in all copies of the internal header
<yvals.h>
, then rebuild the libraries.
Since this disables quite a bit of file buffering, your
program may well run slower. And you might still not
like the way the atomic writes interleave; but you do
have tighter control over file modifications with this
option.
In the Standard C++ Library, all input/output is mediated
by iosrteam objects of template types
basic_istream
,
basic_iostream
, and
basic_ostream
.
Atomic operations on these objects, such as extractions and insertions,
are protected against simultaneous access from different threads.
They are also synchronized with operations directly on
the FILE
object that underlies each iostream object.
(In this implementation
there is no need to call the iostream member function
sync_with_stdio
to ensure such synchronization, nor is there any performance benefit
in disabling it.)
Thus, you can safely share iostream objects across
threads without worrying about loss of integrity. But once again you
might not like the way file accesses interleave, so you
typically need additional thread locks at the appropriate
level of granularity.
If you want changes to the file to be reflected after
each atomic write, you must change the definition of the
macro _IOSTREAM_OP_LOCKS
from 0
to 1
in all copies of the internal header
<yvals.h>
, then rebuild the libraries.
Since this disables quite a bit of file buffering, your
program may well run slower. And you might still not
like the way the atomic writes interleave; but you do
have tighter control over file modifications with this
option.
For the container objects
defined in the Standard C++ Library, such as
STL Containers
and objects of template class
basic_string
,
this implementation
follows the widely adopted practices spelled out for
SGI STL:
Thus, no attempt is made to ensure that atomic operations on container objects are thread safe; but it is easy enough to make shared container objects that are thread safe at the appropriate level of granularity.
See also the Table of Contents and the Index.
Copyright © 1992-2013 by P.J. Plauger. All rights reserved.