Is local static variable initialization thread-safe in C++11? [duplicate]
The relevant section 6.7:
such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. [...] If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
Then there's a footnote:
The implementation must not introduce any deadlock around execution of the initializer.
So yes, you're safe.
(This says nothing of course about the subsequent access to the variable through the reference.)
Local Static variable initialization is thread safe [duplicate]
Yes, it will be thread safe, but only since C++11. Static variables are initialized in thread safe way, they are often also called magic statics.
For more see here: http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once).
Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison.
Also - in your code you call CreateEmployee(); during initialization of static i
, and CreateEmployee(
also initializes a static variable. This should also be OK, you can find in standard following footnote:
The implementation must not introduce any deadlock around execution of the
initializer.
As to your second question, from the code you have shown I dont see that it is OK to use static variable as a way to gain thread safety.
Are you aware that specifying a variable as static inside function body allows you to assign it only once? This means your CreateEmployee() will always return the same Employee instance.
Cost of thread-safe local static variable initialization in C++11?
A look at the generated assembler code helps.
Source
#include <vector>
std::vector<int> &get(){
static std::vector<int> v;
return v;
}
int main(){
return get().size();
}
Assembler
std::vector<int, std::allocator<int> >::~vector():
movq (%rdi), %rdi
testq %rdi, %rdi
je .L1
jmp operator delete(void*)
.L1:
rep ret
get():
movzbl guard variable for get()::v(%rip), %eax
testb %al, %al
je .L15
movl get()::v, %eax
ret
.L15:
subq $8, %rsp
movl guard variable for get()::v, %edi
call __cxa_guard_acquire
testl %eax, %eax
je .L6
movl guard variable for get()::v, %edi
movq $0, get()::v(%rip)
movq $0, get()::v+8(%rip)
movq $0, get()::v+16(%rip)
call __cxa_guard_release
movl $__dso_handle, %edx
movl get()::v, %esi
movl std::vector<int, std::allocator<int> >::~vector(), %edi
call __cxa_atexit
.L6:
movl get()::v, %eax
addq $8, %rsp
ret
main:
subq $8, %rsp
call get()
movq 8(%rax), %rdx
subq (%rax), %rdx
addq $8, %rsp
movq %rdx, %rax
sarq $2, %rax
ret
Compared to
Source
#include <vector>
static std::vector<int> v;
std::vector<int> &get(){
return v;
}
int main(){
return get().size();
}
Assembler
std::vector<int, std::allocator<int> >::~vector():
movq (%rdi), %rdi
testq %rdi, %rdi
je .L1
jmp operator delete(void*)
.L1:
rep ret
get():
movl v, %eax
ret
main:
movq v+8(%rip), %rax
subq v(%rip), %rax
sarq $2, %rax
ret
movl $__dso_handle, %edx
movl v, %esi
movl std::vector<int, std::allocator<int> >::~vector(), %edi
movq $0, v(%rip)
movq $0, v+8(%rip)
movq $0, v+16(%rip)
jmp __cxa_atexit
I'm not that great with assembler, but I can see that in the first version v
has a lock around it and get
is not inlined whereas in the second version get
is essentially gone.
You can play around with various compilers and optimization flags, but it seems no compiler is able to inline or optimize out the locks, even though the program is obviously single threaded.
You can add static
to get
which makes gcc inline get
while preserving the lock.
To know how much these locks and additional instructions cost for your compiler, flags, platform and surrounding code you would need to make a proper benchmark.
I would expect the locks to have some overhead and be significantly slower than the inlined code, which becomes insignificant when you actually do work with the vector, but you can never be sure without measuring.
C++11 Thread-safe initialization of function-local static const objects
Since C++11 all static local variables are guaranteed to be initialized only once in a thread-safe manner.
As per cppreference:
If multiple threads attempt to initialize the same static local
variable concurrently, the initialization occurs exactly once (similar
behavior can be obtained for arbitrary functions withstd::call_once
).
Note: usual implementations of this feature use variants of the
double-checked locking pattern, which reduces runtime overhead for
already-initialized local statics to a single non-atomic boolean
comparison.
So, for your questions:
- yes
- yes
- yes
C++11 Singleton. Static variable is thread safe? Why?
In C++11 (and forward), the construction of the function local static AppSettings
is guaranteed to be thread-safe. Note: Visual Studio did not implement this aspect of C++11 until VS-2015.
The compiler will lay down a hidden flag along side of AppSettings
that indicates whether it is:
- Not constructed.
- Being constructed.
- Is constructed.
The first thread through will find the flag set to "not constructed" and attempt to construct the object. Upon successful construction the flag will be set to "is constructed". If another thread comes along and finds the flag set to "being constructed", it will wait until the flag is set to "is constructed".
If the construction fails with an exception, the flag will be set to "not constructed", and construction will be retried on the next pass through (either on the same thread or a different thread).
The object instance
will remain constructed for the remainder of your program, until main()
returns, at which time instance
will be destructed.
Every time any thread of execution passes through AppSettings::GetInstance()
, it will reference the exact same object.
In C++98/03, the construction was not guaranteed to be thread safe.
If the constructor of AppSettings
recursively enters AppSettings::GetInstance()
, the behavior is undefined.
If the compiler can see how to construct instance
"at compile time", it is allowed to.
If AppSettings
has a constexpr
constructor (the one used to construct instance
), and the instance
is qualified with constexpr
, the compiler is required to construct instance
at compile time. If instance
is constructed at compile time, the "not-constructed/constructed" flag will be optimized away.
Is C++ static member variable initialization thread-safe?
It's more a question of function-scoped static variables vs. every other kind of static variable, rather than scoped vs. globals.
All non-function-scope static variables are constructed before main(), while there is only one active thread. Function-scope static variables are constructed the first time their containing function is called. The standard is silent on the question of how function-level statics are constructed when the function is called on multiple threads. However, every implementation I've worked with uses a lock around the constructor (with a twice-checked flag) to guarantee thread-safety.
C++: Is initializing a static local variable with a IIFE thread-safe?
Yes. C++11 (and above) guarantees no data races between multiple threads trying to initialize a static local variable. If the code inside your lambda is thread-safe, the initialization will be as well.
Using a lambda, function call, or constructor doesn't change the thread-safety of the initialization.
Related Topics
Start Thread With Member Function
What Is the "--≫" Operator in C++
Is Gcc 4.8 or Earlier Buggy About Regular Expressions
How to Detect Unsigned Integer Overflow
Is There a Performance Difference Between I++ and ++I in C++
How to Create a Contiguous 2D Array in C++
Best Practices For Circular Shift (Rotate) Operations in C++
How to Test Whether Stringstream Operator≫≫ Has Parsed a Bad Type and Skip It
Why Do People Say There Is Modulo Bias When Using a Random Number Generator
Why Have Header Files and .Cpp Files
In What Cases Do I Use Malloc And/Or New
"Unpacking" a Tuple to Call a Matching Function Pointer
What Does the Explicit Keyword Mean
Strange Output in Comparison of Float With Float Literal
How Come a Non-Const Reference Cannot Bind to a Temporary Object