1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
Before using the hi(once_flag)tt(std::once_flag) and the
hi(call_once)tt(std::call_once) function, introduced in this section, the
tthi(mutex) header file must be included.
In single threaded programs the initialization of global data not necessarily
happens at the same point in code. An example is the initialization of the
object of a singleton class (cf. em(Gamma et al.) (1995), Design Patterns,
Addison-Wesley). Singleton classes may define a single static pointer data
member tt(Singleton *s_object), pointing to the singleton's object, and may
offer a static member tt(instance), implemented something like this:
verb(
Singleton &Singleton::instance()
{
return s_object ?
s_object
:
(s_object = new Singleton);
}
)
With multi-threaded programs this approach immediately gets complex. For
example, if two threads call tt(instance) at the same time, while tt(s_object)
still equals 0, then both may call tt(new Singleton), resulting in one
dynamically allocated tt(Singleton) object becoming unreachable. Other
threads, called after tt(s_object) was initialized for the first time, may
either return a reference to that object, or may return a reference to the
object initialized by the second thread. Not exactly the expected behavior of
a singleton.
Mutexes (cf. section ref(MUTEX)) can be used to solve these kinds of problems,
but they result in some overhead and inefficiency, as the mutex must be
inspected at each call of tt(Singleton::instance).
When variables must dynamically be initialized, and the initialization should
take place only once the tt(std::once_flag) type and the tt(std::call_once)
function should be used.
The tt(call_once) function expects two or three arguments:
itemization(
it() The first argument is a tt(once_flag) variable, keeping track of the
actual initialization status. The tt(call_once) function simply returns if
the tt(once_flag) indicates that initialization already took place;
it() The second argument is the address of a function which must be called
only once. This function may be a free function or it may be the address of a
class member function;
it() If the second argument is the address of a class member function,
then the object for which the member function should be called must be
provided as tt(call_once's) third argument.
)
A thread-safe implementation of the singleton's tt(instance) function can
now easily be designed (using in-class implementations for brevity):
verb(
class Singleton
{
static std::once_flag s_once;
static Singleton *s_singleton;
...
public:
static Singleton *instance()
{
std::call_once(s_once, []{s_singleton = new Singleton;} );
return s_singleton;
}
...
};
)
However, there are additional ways to initialize data, even for multi-threaded programs:
itemization(
it() First, suppose a constructor is declared with the ti(constexpr)
keyword (cf. section ref(CONSTEXPR)), satisfying the requirements for constant
initialization. In this case, a static object, initialized using that
constructor, is guaranteed to be initialized before any code is run as part of
the static initialization phase. This used by tt(std::mutex), as it eliminates
the possibility of race conditions when global mutexes are initialized.
it() Second, a static variable defined within a compound statement may be
used (e.g., a static local variable within a function body). Static
variables hi(static variable: initialization) defined within a compound
statement are initialized the first time the function is called at the point
in the code where the static variable is defined. Here is an example:
verbinclude(-a examples/staticlocal.cc)
This feature causes a thread to wait automatically if another thread is
still initializing the static data (note that em(non-static) data never cause
problems, as non-static local variables only exist within their own thread of
execution).
)
|