C ++ static initializer is thread safe

Usually when I try to initialize a static variable

class Test2 {
public:
    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 flow safe during static variable initialization?
  • Is there a better way to statically initialize a string list instead of using a separate static function (init)?

Although the initialization has to happen before the main function (Hence there cannot be threads to access init concurrently), I am concerned that:

  • I have an exe application.
  • My exe will load a.dll, b.dll and c.dll files
  • a / b / c.dll will in turn load common.dll. The above code is inside common.dll
  • I've already checked. Since the 3 dlls are in the same process, they will refer to the same static variable (vector).
  • In this case, in order to prevent three DLLs from concurrently accessing init (can I view them as 3 threads? Although it doesn't make sense to begin with), for the init function, should I use a critical section to protect it?

I am using Windows XP, VC6 and VC2008 compiler.

+2


a source to share


7 replies


I asked a similar question a while ago:

LoadLibrary and static globals



When it comes to DLLs, static initialization and DllMain calls are bracketed with an internal critical section, so they are thread safe. The second thread will wait until the first is executed before it loads the DLL.

So your static init is safe.

+1


a source


Is the following code flow safe when initializing a static variable?
It depends entirely on your compiler. The standard doesn't say anything about multithreading.

Is there a better way to statically initialize a string list instead of using a separate static function (init)?
First, you need to remove the __s (These two underscores) in front of the name; any name starting with __ is reserved by the standard. (There are some situations where underscores alone are not allowed - it is best to simply avoid underscores of names on names)



It totally depends on whether the vector needs to be modified at runtime. If not, perhaps you are just using the built-in array:

static const char *strings[] = {
    "string1",
    "string2",
    "string3"
};

      

+7


a source


No code with double underscore identifier names can be considered safe for anything, as it violates standard rules for implementation-reserved names. The same is true for a name with a leading underscore followed by an uppercase letter, but other names starting with an underscore may be used if they are not in the global namespace. See Standard 17.4.3.1.2. These limitations continue in the Committee's latest final draft for C ++ 0x, in accordance with 17.6.3.3.2.

+2


a source


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. The code should work fine.

I usually use a separate init function and use boost :: call_once to ensure that it is only called once.

boost::once_flag boost_once_flag = BOOST_ONCE_INIT; 
boost::call_once(init_static_var, boost_once_flag);

      

+2


a source


it is not thread safe because multiple threads can call it at the same time and you are initializing stringList twice. Use thread synchronization.

0


a source


Check out my quick example demonstrating that your container is not 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;
}

      

0


a source


If you need access to these lines prior to main launch, this may or may not work, depending on how the compiler / linker order is initialized.

Better would be either:

  • Call init from the main one so you know the state of the program as it pops up.
  • Use a singleton whose constructor fills strings into a non-static member vector. Then when you first create the singleton (to eliminate the possibility of an initialization problem, after static initialization), it will add the lines.
0


a source







All Articles