Protecting Data During Initialization
If your data only needs protecting during its initialization, using a mutex is not the answer. Doing so only leads to unnecessary synchronization after initialization is complete. The C++0x standard provides several ways of dealing with this.
First, suppose your constructor is declared with the new constexpr keyword and satisfies the requirements for constant initialization. In this case, an object of static storage duration, initialized with that constructor, is guaranteed to be initialized before any code is run as part of the static initialization phase. This is the option chosen for std::mutex, because it eliminates the possibility of race conditions with initialization of mutexes at a global scope:
class my_class
{
int i;
public:
constexpr my_class():i(0){}
my_class(int i_):i(i_){}
void do_stuff();
};
my_class x; // static initialization with constexpr constructor
int foo();
my_class y(42+foo()); // dynamic initialization
void f()
{
y.do_stuff(); // is y initialized?
}
Your second option is to use a static variable at block scope. In C++0x, initialization of block scope static variables happens the first time the function is called. If a second thread should call the function before the initialization is complete, then that second thread has to wait:
void bar()
{
static my_class z(42+foo()); // initialization is thread-safe
z.do_stuff();
}
If neither options apply (perhaps because the object is dynamically allocated), then it's best to use
std::call_once and
std::once_flag. As the name suggests, when std::call_once is used in conjunction with a specific instance of type
std::once_flag, the specified function is called exactly once:
my_class* p=0;
std::once_flag p_flag;
void create_instance()
{
p=new my_class(42+foo());
}
void baz()
{
std::call_once(p_flag,create_instance);
p->do_stuff();
}
Just as with the
std::thread constructor,
std::call_once can take function objects instead of functions, and can pass arguments to the function. Again, copying is the default, and you have to use
std::ref if you want a reference.