C++规范编码引涉语法点 之(3)静态变量初始化问题

【1】C++函数内的静态变量初始化以及线程安全问题

(1)如果是编译时和加载时初始化, 是不会存在线程安全问题的;  
         因为这两种初始化一定发生在Main函数执行之前, 这个时候尚未进入程序运行空间; 而这些初始化一定是在单线程环境下操作的!  都是在执行C Runtime的startup代码中的void mainCRTStartup(void)函数时所在的OS系统加载程序时的主线程空间上发生的!
(2)如果是运行时初始化, 因为无法保证访问这个静态变量所在的局部函数/全局函数/类成员函数/类静态成员函数  一定只会从某个特定的线程中被访问, 因此, 就一定会存在"线程安全"的问题!

【2】静态变量初始化的时机
(1)静态变量的内存分配和初始化:
       对于C语言的全局和静态变量,不管是否被初始化,其内存空间都是全局的;如果初始化,那么初始化发生在任何代码执行之前,属于编译期初始化。由于内置变量无须资源释放操作,仅需要回收内存空间,因此程序结束后全局内存空间被一起回收,不存在变量依赖问题,没有任何代码会再被执行!
        C++引入了对象,这给全局变量的管理带领新的麻烦。C++的对象必须有构造函数生成,并最终执行析构操作。由于构造和析构并非分配内存那么简单,可以说相当复杂,因此何时执行全局或静态对象(C++)的构造和析构呢?这需要执行相关代码,无法在编译期完成,因此C++标准规定:全局或静态对象当且仅当对象首次用到时才进行构造,并通过atexit()来管理对象的生命期,在程序结束之后(如调用exit,main),按FILO顺序调用相应的析构操作!

总结:
       全局变量、文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。

(2)静态变量初始化的线程安全性说明:
       非局部静态变量一般在main执行之前的静态初始化过程中分配内存并初始化,可以认为是线程安全的;
       局部静态变量在编译时,编译器的实现一般是在初始化语句之前设置一个局部静态变量的标识来判断是否已经初始化,运行的时候每次进行判断,如果需要初始化则执行初始化操作,否则不执行。这个过程本身不是线程安全的。

注1:C++11标准针规定了局部静态变量初始化需要保证线程安全,具体说明如下:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution 
shall wait for completion of the initialization
    新的编译器大多对C++11的标准支持,因此也保证了这一点,但C++03标准之前并无此说明,所以很多旧版本的编译器并不能完全支持。
注:VS2008 测试多线程的条件下虽然只有一个线程执行一次初始化,但非初始化的线程并不会等待初始化结束,而是立即返回未正确初
始化的静态对象。
    针对局部静态变量初始化的线程安全性,g++编译器的实现相当于使用了一个全局锁来控制一个局部静态变量的标识(标识用来判定
是否已经初始化)。详情参考:http://www.cnblogs.com/xuxm2007/p/4652944.html

说明&&建议:
       以前的标准都没有规定局部静态变量的初始化在并发模式下是否安全,很多旧版本的编译器并没有处理它的并发安全问题。因此在不支持C++11标准的编译环境下,多线程程序最好不要使用需要明显初始化的局部静态变量(对象),如果需要使用(比如单例模式中),则可以考虑使用一个全局锁或静态成员变量锁,最好不要使用局部静态变量锁,因为其本身存在一个构造的问题,多个线程获取实例的时候,可能会出现一个线程在进行锁对象构造,另一个线程则避开了构造,在锁对象还没有完全构造完成的情况下就lock了,这个时候的行为能不能成功锁定取决于锁的实现了,虽然一般的实现不会出现问题但终归不是很严谨。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值