Thread Safety


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.

Standard C Input/Output

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.

Standard C++ Input/Output

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.

Containers

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.