静态线程安全和初始化顺序

时间:2015-04-16 13:53:07

标签: c++ multithreading constructor initialization

我在我正在处理的一些代码中遇到了一个线程问题。 MyStruct在多个线程上构造,导致程序有时在staticFunc中崩溃。代码正在访问不安全的变量值(MSVC 2012编译器),这是一个惊喜。

struct MyStruct : baseClass
{
    MyStruct() : baseClass(myFunc())
    {
    }

    static int* myFunc()
    {
        static int value[const_size] = get_array();
        // Do some complex stuff to value.
        return &value[0];
    }
};

我的问题是,解决这个问题的最佳方法是什么? 我的第一个想法是一起删除静态声明。 这实际上允许我写的单元测试通过。

struct MyStruct : baseClass
{
    MyStruct() : baseClass(myFunc())
    {
    }

    int* myFunc()
    {
        this->value = get_array();
        // Do some complex stuff to value.
        return &this->value[0];
    }

    int value[const_size];
};

但是我担心,如果在构造/初始化之前使用了值,那么它是不是未定义的行为?

施工顺序:

  1. baseClass构造函数调用myFunc()
  2. myFunc()使用值。
  3. 等待!值尚未由MyStruct的构造函数初始化!
  4. 这是发生了什么,或者我错过了什么,如果是这样,解决这个问题的正确方法是什么?静态互斥?

    编辑:我正在使用MSVC 2012(静态不是线程安全的)并且移动到不同的编译器不是一个选项。

2 个答案:

答案 0 :(得分:0)

第一个问题是你是想在不同线程之间共享值,还是每个线程都可以存储自己的值。

如果要跨线程共享值,则需要使用Critical Section保护所有访问。

如果您对使用不同值的不同线程没问题,则需要查看线程本地存储。 c ++ 11为此提供了thread_local,但我不确定MSVC是否支持此功能。否则你可以使用TlsAlloc,TlsGetValue,TlsSetValue和TlsFree。

答案 1 :(得分:0)

使用std::call_once,它实际上是为解决这个问题而发明的。从内存开始,您不需要针对文件范围静态的线程安全静态,因为它们必须在输入main之前进行初始化,因此任何人都不可能在您的代码中有任何线程它被初始化了。

// Place the func_flag at file scope.
static std::once_flag my_func_flag;
class blah {
    static const int* myFunc()
    {
        static int value[const_size];
        std::call_once(my_func_flag, [] {
            // perform ALL mutations of value in here.
            value = get_array();
        });
        // Read-only from here on.
        return &value[0];
    }
};