Protecting data during initialization
If your data needs to be protected during initialization, you can no longer use a mutex. This can cause unnecessary synchronization after the initialization has ended. C++0X provides a number of ways to protect data during initialization.
1) Assume that your constructor is declared with the CONSTEXPR keyword and satisfies the condition of the constant initialization. In this case, the object of a static store will ensure that it is initialized before other code runs in the static initial phase. This is the best choice for Std::mutex, because it eliminates the possibility of confusion when global mutex initialization occurs.
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?
}
2 use a static variable in a block scope. In c++0x, the static variable of a block scope is initialized when the function is first invoked. If another thread attempts to invoke the function before it completes initialization, it must wait.
void bar()
{
static my_class z(42+foo()); // initialization is thread-safe
z.do_stuff();
}
3 If none of the above applies (the object may be dynamically created), then it is best to use std::call_once and Std::once_flag. As you can see from the name, Std::call_once is used to collaborate with a Std::once_flag instance, and the specified function will only be executed 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();
}
Like the Std::thread constructor, Std::call_once can also accept function objects as arguments and accept multiple parameters. Again, the default is to transfer copies. If you want to pass a reference, use Std::ref.