C++ Static Initializer - Is it thread safe
Usually, when I try to initialize a static variable
class Test2 {
p开发者_开发百科ublic:
static vector<string> stringList;
private:
static bool __init;
static bool init() {
stringList.push_back("string1");
stringList.push_back("string2");
stringList.push_back("string3");
return true;
}
};
// Implement
vector<string> Test2::stringList;
bool Test2::__init = Test2::init();
- Is the following code thread safe, during static variable initialization?
- Is there any better way to static initialize stringlist, instead of using a seperate static function (init)?
Although the initialization shall happen before main function (Hence, there can be no threads to simultaneous access the init), my concern is that :
- I have an exe application.
- My exe application will load a.dll, b.dll and c.dll
- a/b/c.dll, in turn will load common.dll. The above code are inside common.dll
- I had already verify. Since 3 dll are within single process, they will be referring to the same static variable (vector).
- In this case, to prevent 3 dlls from simultaneous access init (Can I view them as 3 threads? Although doesn't make sense at first thought), for the init function, shall I use a critical section to protect it?
I am using Windows XP, VC6 and VC2008 compiler.
Is the following code thread safe, during static variable initialization?
That depends entirely upon your compiler. The standard says absolutely nothing about multithreading.
Is there any better way to static initialize stringlist, instead of using a seperate static function (init)?
First, you need to remove the __s (That's two underscores) in front of the name; any name beginning with __ is reserved according to the standard. (There are some situations where a single underscore is not allowed either -- it's best just to avoid leading underscores on names altogether)
That depends entirely on whether the vector needs to change during its runtime. If not, you're probably off just using a builtin array:
static const char *strings[] = {
"string1",
"string2",
"string3"
};
No code using identifier names with a double underscore can be considered safe for anything, as it violates the Standard's rules on names reserved for the implementation. The same is true for a name with a leading underscore followed by an uppercase letter, but other names beginning with an underscore may be used, as long as they're not in the global namespace. See the Standard, 17.4.3.1.2. These restrictions are continued in the latest Final Committee Draft for C++0x, under 17.6.3.3.2.
Local static initialization is not thread safe. See http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx.
Global static initialization is usually thread safe. You code should work OK.
I normally use a separate init function, and use boost::call_once to ensure that it is called only once.
boost::once_flag boost_once_flag = BOOST_ONCE_INIT;
boost::call_once(init_static_var, boost_once_flag);
I asked a similar question a while back:
LoadLibrary and Static Globals
When it comes to DLLs, static initialization and the call to DllMain is bracketed by an internal critical section, so they are thread-safe. A second thread will wait until the first is done before it loads the DLL.
So in short, your static init is safe.
it is not thread-safe because several threads can call it simultaneously and you will have stringList initialized twice. Use thread synchronization.
Check out my quick example that demonstrates that your container isn't thread safe.
#include <boost/thread.hpp>
#include <vector>
#include <string>
class NotThreadSafeContainer {
static std::vector<std::string> strings;
static bool is_initialized;
public:
NotThreadSafeContainer() {
if (!is_initialized) {
is_initialized = true;
strings.push_back("string1");
strings.push_back("string2");
strings.push_back("string3");
strings.push_back("string4");
strings.push_back("string5");
}
}
};
bool NotThreadSafeContainer::is_initialized = false;
std::vector<std::string> NotThreadSafeContainer::strings;
void thread_routine() {
while (true) {
// Wow! A container
NotThreadSafeContainer ts_container;
}
};
void main() {
// Uncomment this to remove thread desync errors
// boost::once_flag once_flag = 0;
// boost::call_once(once_flag, thread_routine);
// Start some threads
boost::thread t1(thread_routine);
boost::thread t2(thread_routine);
boost::thread t3(thread_routine);
NotThreadSafeContainer ts_container;
bool SET_BREAKPOINT_HERE;
}
If you need access to these strings prior to main running, this may or may not appear to work depending on how the compiler/linker order initialization.
Better would be to either:
- Call init from main so you know the program state when it's populating.
- Use a singleton whose constructor populates the strings into a non-static member vector. Then when you first instantiate the singleton (to remove possibility of initialization problems, after static initialization) it will add the strings.
精彩评论