【问题标题】:How to implement thread safe local static variable in C++03?如何在 C++03 中实现线程安全的局部静态变量?
【发布时间】:2013-09-20 08:05:54
【问题描述】:

在 C++11 之前,局部静态变量不是线程安全的。由于某些不可重入函数的结果,我需要在性能关键函数中初始化一个静态变量。

我希望看到一个静态变量初始化,它使用互斥锁或其他同步原语,但对常规静态变量进行一些机会性检查,以减少在变量早已初始化的典型情况下使用互斥锁。似乎 GCC 为 C++11 实现了类似的东西,如 Static initialization and thread safety 中所述,但那里列出的代码并不完整,只是汇编。

注意:有很多问题询问静态变量初始化是否在 Stack Overflow 上是原子的,但他们似乎对“否”的答案感到满意,而且他们似乎没有给出实际的解决方案(如 C++ Static Initializer - Is it thread safe) .

【问题讨论】:

  • C++03 中没有可移植的解决方案,因为在 C++03 中没有线程的概念。您必须向您的操作系统寻求帮助。而且我不知道你的操作系统,所以我帮不了你。
  • @knivil 我正在寻找一种实用的解决方案,不一定是便携式解决方案(但将非便携式部分隔离起来会很方便)。假设我有一个可移植的互斥锁(这听起来很合理,因为几乎所有平台都有某种互斥锁),那么线程安全解决方案会是什么样子?
  • 如果你不想提升,那么 pthread_once 用于 POSIX 和 .... (?) 用于 Windows。
  • 我尝试自己编写一个基于互斥锁的实现,并很快意识到它比看起来更复杂。问题是互斥对象本身在初始化之前不能使用,它的初始化需要是线程安全的,或者在函数被调用之前完成。这可能就是 GCC 最初选择全局互斥锁的原因。我可能会检查最近的 GCC 是如何解决这个问题的(或者 boost 或 std call_once 是如何实现的)。

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


【解决方案1】:

我在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 语言标准所要求的静态块范围变量的零初始化。在MSVC 2014 提供实际编译器和运行时支持之前,上述宏将让您安全地使用“魔法”静态。

【讨论】:

    【解决方案2】:

    您可以将静态数据放在一个函数中并利用 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)可用于运行 例行程序恰好一次。这可用于初始化数据 线程安全的方式。

    【讨论】:

    • 我不熟悉 boost::call_one。我假设这将确保初始化不会被调用两次,但它是否也确保“数据”在访问时已经初始化?
    • 数据将在第一次函数调用时静态初始化,之后调用一次。
    • std::call_once docs 说“在成功完成上述所选函数的执行之前,组中没有调用返回”这是我正在寻找的保证,但我在boost docs 中看不到它.即使它有效,我更喜欢没有提升的解决方案。我认为应该可以用互斥体模仿什么 boost::call_once?
    • @Suma:如果我是你,我会选择 boost。它保证是正确的,并且可能是您的编译器上最快的实现——不管它是什么。而且它是便携式的。一旦你的编译器支持它,你就可以将它直接切换到 std::call_once 。但是是的,可以使用互斥锁来完成。
    猜你喜欢
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多