Synchronization of global and static variables

Because global and static variables are shared by all threads in an address space, the synchronization considerations apply to them as well. Any globals (including static class members) that are written or read by more than one thread must be protected by locking or other means. An exception is a const global with file scope; these are initialized at static constructor time, when only one thread is running, and don't change afterwards.

A particularly insidious problem occurs with static variables defined inside functions. Local static variables with an initializer are initialized the first time the function is called, not at static constructor time. If more than one thread can make the initial call, they might try to do so simultaneously, resulting in a crash. Local statics are a good way to cut down on overhead, but you must consider this concurrency issue if the function can be called from multiple threads. See "Avoid static objects" on page 86 for more information.

If a function can be called by multiple threads, you can only use primitive C types that don't have constructors, such as pointers or numbers, and they must be initialized by compile-time constant expressions; they are initialized before your code starts running. To use an object, you can write code similar to:

      void Foo() {
          static TBar *gBar = NIL;
          gProtect.Acquire();         // Semaphore can't be function static
          if ( gBar == NIL )
              gBar = new TBar();
          gProtect.Release();
          Function( gBar );           // Safe: above guarantees that gBar is valid
      }
The semaphore itself can't be static inside the function because it would have the same synchronization problem. If your class is used by multiple threads, you might already have a semaphore or monitor to protect the initialization.

CAUTION You might be tempted to optimize the previous example by testing for gBar being NIL before you acquire the lock, because the most frequent case is that it has already been initialized. This is not safe! Although gBar might be non-NIL, that does not mean it has settled into a correct state. Enclosing the initialization sequence in an if(gBar==NIL) test makes the function call's reference to gBar unsafe. Unsynchronized access is subtle and dangerous!


[Contents] [Previous] [Next]
Click the icon to mail questions or corrections about this material to Taligent personnel.
Copyright©1995 Taligent,Inc. All rights reserved.

Generated with WebMaker