如何在C ++ 03中实现线程安全的本地静态变量?

时间:2013-09-20 08:05:55

标签: c++ multithreading static initialization c++03

在C ++ 11之前,本地静态变量不是线程安全的。我需要在一个性能关键函数中初始化一个静态变量,其结果是一些非重入函数。

我希望看到一个使用互斥锁或其他同步原语的静态变量初始化,但是在很久以前已经初始化变量的情况下,对常规静态变量进行机会检查以减少常规情况下的互斥使用。似乎GCC为{+ 3}}中提到的C ++ 11实现了类似的东西,但是那里列出的代码并不完整,只是汇编。

注意:有很多问题要求静态变量初始化在Stack Overflow上是原子的,但它们似乎满足于答案" no"它们似乎没有显示出实际的解决方案(如Static initialization and thread safety)。

2 个答案:

答案 0 :(得分:4)

您可以将静态数据放在函数中并使用boost :: once:

int& get_static() {
    static boost::once_flag once_flag = BOOST_ONCE_INIT;
    static int* data;

    struct Initialize
    {
        static void apply() {
            data = new int(1);
        }
    };
    boost::call_once(once_flag, &Initialize::apply);
    return *data;
}

数据将在第一次函数调用时初始化,之后调用一次。

http://www.boost.org/doc/libs/1_32_0/doc/html/call_once.html中:

  

call_once函数和once_flag类型   (静态初始化为BOOST_ONCE_INIT)可用于运行a   例程一次。这可用于初始化a中的数据   线程安全的方式。

答案 1 :(得分:4)

我在follow-up中对问题中引用的博客文章进行了讨论。如果由于某种原因你不能使用boost::call_once你的块范围的静态是一个指针,POD,或者有一个线程安全的构造函数,你可以编写GCC会发出的相同的初始化保护代码:

// Define a static local variable once, safely, for MSVC
//
// This macro is necessary because MSVC pre-2013 doesn't
// properly implement C++11 static local initialization.
// It is equivalent to writing something like
//
//     static type var = stmt;
//
// in a compliant compiler (e.g. GCC since who knows when)

// States for lock checking
enum { uninitialized = 0, initializing, initialized };

// Preprocessor hackery for anonymous variables
#define PASTE_IMPL(x, y) x ## y
#define PASTE(x, y) PASTE_IMPL(x, y)
#define ANON_VAR(var) PASTE(var, __LINE__)

#define STATIC_DEFINE_ONCE(type, var, stmt)                     \
    static type var;                                            \
    static int ANON_VAR(state);                                 \
    bool ANON_VAR(cont) = true;                                 \
    while (ANON_VAR(cont)) {                                    \
        switch (InterlockedCompareExchange(&ANON_VAR(state),    \
                initializing, uninitialized)) {                 \
        case uninitialized:                                     \
            var = stmt;                                         \
            InterlockedExchange(&ANON_VAR(state), initialized); \
            ANON_VAR(cont) = false;                             \
            break;                                              \
        case initializing:                                      \
            continue;                                           \
        case initialized:                                       \
            ANON_VAR(cont) = false;                             \
            break;                                              \
        }                                                       \
    } do { } while (0)

你可以像

一样使用它
void concurrently_accessed() {
    STATIC_DEFINE_ONCE(int, local_var, thread_unsafe_initializer());
    // ...
}

这种方法利用了静态块范围变量的零初始化,这是C语言标准所要求的。以上宏将让您安全地使用" magic"静态直到实际的编译器和运行时支持到达MSVC 2014