[cpp-threads] Globals and Threads

Andrei Alexandrescu andrei at metalanguage.com
Sun Oct 16 00:06:59 BST 2005


> I have been thinking about that little problem of globals related
> to threads. Globals will need to be placed in a thread-shared memory
> region, no matter how many threads are used in a C++ program.
> 
> My understanding is that this will result in a change in the way globals
> are handled. 

I don't think the ripples are huge. As long as we provide a way of 
talking about order of initialization, the problems with several threads 
accessing a global are similar to the problems with several threads 
accessing whatever shared object through whatever sharing means.

> While thinking about globals, I stumbled across the problem of static
> locals. What is the implication when a function with local static variables
> is called from several threads? This could result in race conditions if
> one thread is updating the variable while the other reads from the
> variable. Simply put, normal local static variables are not thread-safe.

We've talked about this. No shared data is automagically thread-safe btw 
:o). As far as I remember, there were three possible solutions that we 
discussed:

1. The compiler always takes care of the synchronization:

Widget * Foo() {
   static Widget w; // initialized thread-safe
   return &w;
}

Pro: safe. Con: sometimes you don't want the synchronization, such as 
when you know the object will be accessed only from one thread or in the 
case of benign races.

2. Add some means to instruct the compiler to ensure that one and only 
one thread initializes the shared object, e.g.:

Widget * Foo() {
   static __threadsafe__ Widget w;
   return &w;
}

and leave things "unsafe" by default.

3. Leave things as they are and allow users to guard things manually 
with a statically-initialized bool:

Widget * Foo() {
   static bool initialized = false;
   static Widget result = 0;
   if (std::assign_once(initialized, true)) {
     // Code executed only once
     static Widget w;
     result = &w;
   }
   return result;
}

I incline towards 3 because it's most flexible and doesn't require 
syntax changes.

> Static data members provide a challenge similar to local static variables.

The challenges are different. Static data member have global semantics.

> Separate instances of a class containing a static data member may be 
> updated and read by separate threads. Static data members will need to be
> placed in memory regions accessible to all threads. Read/write access
> control (either through locks or non-locking mechanisms) would be useful
> for static data members. Providing those mechnisms for non-threaded uses
> would be somewhat inefficient, while not providing them for threaded uses
> would result in incorrect programs.
 >
 > Implicit protection for global and static regins could be provided by
 > a compiler when the compiler can detect the use of several thrads. The
 > biggest problem I see is the use of libraries. Compilers cannot determine
 > if a library will be used in a multi-threaded environment when the 
library
 > is compiled.

I disagree with the mechanisms suggested by this paragraph. We want to 
provide a good model and good primitives for concurrency, not have the 
compiler generate some code it thinks it will transparently take care of 
concurrency on behalf of the client.


Andrei



More information about the cpp-threads mailing list