C ++静态初始化程序 - 它是否是线程安全的

时间:2010-04-28 13:59:51

标签: c++

通常,当我尝试初始化静态变量

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();
  1. 在静态变量初始化期间,以下代码线程是否安全?
  2. 有没有更好的方法来静态初始化stringlist,而不是使用单独的静态函数(init)?
  3. 虽然初始化应该在main函数之前发生(因此,没有线程可以同时访问init),我关心的是:

    1. 我有一个exe应用程序。
    2. 我的exe应用程序将加载a.dll,b.dll和c.dll
    3. a / b / c.dll,反过来会加载common.dll。上面的代码在common.dll
    4. 我已经验证了。由于3个dll在单个进程中,因此它们将引用相同的静态变量(向量)。
    5. 在这种情况下,为了防止3个dll同时访问init(我可以将它们视为3个线程吗?虽然初看起来没有意义),对于init函数,我应该使用临界区来保护它吗?
    6. 我使用的是Windows XP,VC6和VC2008编译器。

7 个答案:

答案 0 :(得分:7)

在静态变量初始化期间,以下代码线程是否安全?
这完全取决于您的编译器。标准对多线程一无所知。

有没有更好的方法来静态初始化stringlist,而不是使用单独的静态函数(init)?
首先,你需要删除名称前面的__s(那是两个下划线);任何以__开头的名称都是根据标准保留的。 (在某些情况下,也不允许使用单个下划线 - 最好只是避免在名称上引入下划线)

这完全取决于向量是否需要在运行时更改。如果没有,你可能只是使用内置数组:

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

答案 1 :(得分:2)

使用带有双下划线的标识符名称的代码不能被认为是安全的,因为它违反了标准关于为实现保留的名称的规则。对于带有前导下划线后跟大写字母的名称也是如此,但是可以使用以下划线开头的其他名称,只要它们不在全局名称空间中即可。见标准,17.4.3.1.2。这些限制在最新的C ++ 0x最终委员会草案中继续,见17.6.3.3.2。

答案 2 :(得分:2)

本地静态初始化不是线程安全的。见http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx

全局静态初始化通常是线程安全的。您的代码应该可以正常工作。

我通常使用单独的init函数,并使用boost :: call_once来确保它只被调用一次。

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

答案 3 :(得分:1)

我回答了一个类似的问题:

LoadLibrary and Static Globals

当谈到DLL时,静态初始化和对DllMain的调用被内部关键部分括起来,因此它们是线程安全的。第二个线程将在加载DLL之前等到第一个线程完成。

简而言之,您的静态初始化是安全的。

答案 4 :(得分:0)

它不是线程安全的,因为几个线程可以同时调用它,并且你将对stringList初始化两次。使用线程同步。

答案 5 :(得分:0)

查看我的快速示例,该示例演示了您的容器不是线程安全的。

#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;
}

答案 6 :(得分:0)

如果您需要在主运行之前访问这些字符串,这可能会也可能不会起作用,具体取决于编译器/链接器顺序初始化的方式。

最好是:

  1. 从main调用init,所以你知道 程序状态何时填充。
  2. 使用构造函数的单例 将字符串填充到一个 非静态成员向量。然后呢 你首先实例化单身人士 (以消除可能性 初始化问题,之后 静态初始化)它会添加 字符串。