Home  >  Q&A  >  body text

C++局部静态变量在什么时候分配内存和初始化?

class A
{
    public:
        A() 
        {
            sleep(10); // 故意让初始化过程放慢
            m_num = 1;
        };
        ~A() {};
        
        void print(int index) { printf("[%d] - %d", index, m_num); }
        
    private:
        int m_num;
}

void func(int index)
{
    static A a; // 静态局部变量,默认构造
    
    // A b;
    //static A a = b; // 静态局部变量,拷贝构造
    a.print(index);
}

int main()
{
    // 三个线程同时执行
    boost::thread trd1(boost::bind(&func, 1));
    boost::thread trd2(boost::bind(&func, 2));
    boost::thread trd3(boost::bind(&func, 3));
    
    sleep(1000);
    return 0;
}

请问,这个类对象局部变量是在什么时候分配内存和初始化的?拷贝构造的时候呢?
网上都说是在程序主函数执行前的静态初始化过程中分配内存并初始化的,但实际测试,当有3个线程同时执行func操作时,会有m_num = 0的输出,证明至少A对象的初始化过程没有完成。

如果是在主函数执行前的静态初始化过程中分配内存和初始化,那么在func中的定义过程貌似只是个赋值的过程?
或者说在静态初始化过程中分配内存,在第一次定义的地方初始化?这样多线程的情况下就会有个竞争初始化的问题?

突然想到了这个问题,求解答,谢谢。

伊谢尔伦伊谢尔伦2714 days ago791

reply all(3)I'll reply

  • 怪我咯

    怪我咯2017-04-17 13:15:52

    Thank you to the two students above for their answers. Combined with your own inquiries and tests, I sorted out the final results and posted them here. If you find anything wrong, please correct me. Thank you.

    Memory allocation and initialization of static variables

    For global and static variables in C language, regardless of whether they are initialized or not, their memory space is global; if initialized, then the initialization occurs before any code is executed and is a compile-time initialization. Since built-in variables do not require resource release operations and only need to reclaim memory space, the global memory space is reclaimed together after the program ends. There is no variable dependency problem and no code will be executed again!

    C++ introduces objects, which brings new troubles to the management of global variables. C++ objects must be generated by a constructor and eventually destructed. Since construction and destruction are not as simple as allocating memory, it can be said to be quite complex, so when should the construction and destruction of global or static objects (C++) be performed? This requires executing relevant code and cannot be completed at compile time. Therefore, the C++ standard stipulates that global or static objects are constructed when and only when the object is used for the first time, and the life cycle of the object is managed through atexit(). After the program ends (such as calling exit, main), call the corresponding destruction operation in FILO order!

    Summary:

    Global variables, static variables in the file domain and static member variables of the class are allocated memory and initialized during the static initialization process before main is executed; local static variables (generally static variables within the function) are allocated for the first time Memory is allocated and initialized when used. Variables here include objects of built-in data types and custom types.

    Thread safety instructions for static variable initialization

    Non-local static variables are generally allocated and initialized during the static initialization process before main is executed, and can be considered thread-safe;

    When compiling local static variables, the compiler implementation generally sets a local static variable identifier before the initialization statement to determine whether it has been initialized. The judgment is made each time during operation. If initialization is required, the initialization operation is performed, otherwise Not executed. The process itself is not thread-safe.

    The C++11 standard stipulates that the initialization of local static variables needs to ensure thread safety. The specific instructions are as follows:
    If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization

    Most new compilers support the C++11 standard, so this is guaranteed. However, there was no such explanation before the C++03 standard, so many older versions of compilers cannot fully support it.

    Note: Although only one thread performs an initialization under the condition of VS2008 testing multi-threading, the non-initialized thread will not wait for the initialization to end, but will immediately return the static object that has not been correctly initialized.

    For the thread safety of local static variable initialization, the implementation of the g++ compiler is equivalent to using a global lock to control the identification of a local static variable (the identification is used to determine whether it has been initialized). For details, please refer to: http://www.cnblogs.com/xuxm2007/p/4652944.html

    Use related:

    Previous standards did not stipulate whether the initialization of local static variables is safe in concurrent mode. Many old versions of compilers did not deal with its concurrency safety issues. Therefore, in a compilation environment that does not support the C++11 standard, it is best not to use local static variables (objects) that require obvious initialization in multi-threaded programs. If you need to use them (such as in singleton mode), you can consider using a global lock. Or static member variable lock, it is best not to use local static variable lock, because it has a construction problem. When multiple threads obtain the instance, one thread may be constructing the lock object, while another thread avoids it. Construction, when the lock object has not been completely constructed, it is locked. Whether the behavior at this time can be successfully locked depends on the implementation of the lock. Although the general implementation will not cause problems, it is not very rigorous after all.

    reply
    0
  • 天蓬老师

    天蓬老师2017-04-17 13:15:52

    Allocate memory when the program starts.
    The constructor is called when the function is called for the first time.
    c++98 does not specify the thread safety of initialization of local static variables. In fact, the compiler creates a hidden variable to identify whether the static variable has been initialized. Check whether the hidden variable has changed each time the function is called.
    C++11 specifies the thread safety of initialization of static variables.
    Please refer to http://stackoverflow.com/questions/246564/what-is-the-lifetime-of-a-static-variable-in-a-c-function

    reply
    0
  • 天蓬老师

    天蓬老师2017-04-17 13:15:52

    Yours is a static variable within the function. It is initialized when the function is executed for the first time. If it is placed outside func, it will be initialized before the main function is executed. Why does 0 appear in your case? Because the initialization is only initialized once, and you are equivalent to calling a method that is only initialized once in three threads. This is thread-unsafe. There is a mark during initialization, and mark A has been initialized. If After initialization, initialization will not be called again. And you used sleep, so 0 will appear. Even if you don't sleep, there may still be a situation of 0, because it is not thread safe. All three threads execute this logic:
    Is a initialized?
    Set the initialization mark and call the constructor initialization without initialization

    It is possible that all three threads call initialization. It is also possible that there is only one initialization. When other threads execute, although the initialization mark is seen, the constructor is not completed, especially when you are sleeping, this is more likely to happen.

    reply
    0
  • Cancelreply