writing

Is C++ Static Variable Initialization Inside a Function Thread-Safe?函数中的 C++ 静态变量初始化是线程安全的吗?

· Performance & internals

Translated from Chinese — .

On this page

Conclusion first: yes, it’s thread-safe, and it even uses double-checked locking, so performance is pretty good.

Exploration

When writing the C++ Singleton pattern, you’ll usually just smoothly write code like this:

class Hello {
public:
    static Hello & instance() {
        static Hello obj;
        return obj;
    }
    
    Hello() {
        printf("constructor");
    }
    ~Hello() {
        printf("destructor");
    }
    
public:
    int x = 0;
};

So when you call Hello::instance(), is it thread-safe? Can the constructor of Hello be guaranteed to run exactly once?

Let’s throw the code into Hopper and take a look.

-w1201

Looks like there’s a lock. So it’s thread-safe :)

Going Further

Notice these few methods:

__cxa_guard_acquire
__cxa_atexit
__cxa_guard_release

__cxa_atexit is likely there to call the destructor when the process exits.

After some searching, you can find this source file cxa_guard.cxx:

https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx

-w609

It implements a double-checked locking via obj_guard.

-w647

inUse stores 0 and 1 in the first address pointed to by obj_guard, implementing the first check.

Found this obj_guard in Hopper:

-w705

This guard variable is a global variable.


Summary

From now on you can happily use this pattern:

    static Hello & instance() {
        static Hello obj;
        return obj;
    }
文章目录

先说结论,是线程安全,而且有double-checked locking,性能还不错。

探索

通常写C++ Singleton模式时,会很流畅的写下如下代码:

class Hello {
public:
    static Hello & instance() {
        static Hello obj;
        return obj;
    }
    
    Hello() {
        printf("constructor");
    }
    ~Hello() {
        printf("destructor");
    }
    
public:
    int x = 0;
};

那么当使用 Hello::instance() 时,是线程安全吗? Hello 的构造函数能确保执行一次吗?

把代码扔到 Hopper 看一下

-w1201

看来是有锁。那就是线程安全咯 :)

进一步

看到这几个方法

__cxa_guard_acquire
__cxa_atexit
__cxa_guard_release

__cxa_atexit 可以是为了在进程结束时调用析构函数。

一番搜索可以找到这个源码 cxa_guard.cxx

https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx

-w609

通过obj_guard 实现了一个double-checked locking

-w647

inUse会通过obj_guard指向内存的第一个地址存取0和1,实现第一次check。

Hopper中找到这个obj_guard

-w705

这个guard variable 是个全局变量。


总结

以后可以愉快的使用这个写法了

    static Hello & instance() {
        static Hello obj;
        return obj;
    }