GCC原子shared_ptr实现

时间:2016-05-12 22:12:46

标签: c++ multithreading c++11 gcc atomic

根据 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57250 ,GCC 4.9支持原子shared_ptr操作。

使用GCC 4.9.2,我能够编译使用原子shared_ptr的程序。 -mcx16标志是必需的,因为x86_64上的GCC实现显然需要cmpxchg16b,这是有道理的,因为我认为shared_ptr上的原子操作需要以原子方式更新指针本身和引用计数同时进行。

但是,当我尝试使用原子shared_ptr库时,它的行为并不像我期望的那样。所以,要么我没有正确使用它,要么GCC实现有缺陷。在大多数情况下,我有99%的信心,我只是做错了,但由于这是一个相对较新的功能,而且由于行为看起来很奇怪,我只有50%的自信在这种情况下,这是我的错。

这是一个创建原子shared_ptr的简单程序,然后在shared_ptr上执行一系列并发读写:

void test()
{
        std::atomic<std::shared_ptr<int>> p(std::shared_ptr<int>(new int(10)));

        std::cout << "Use count : " << p.load().use_count() << std::endl;
        std::cout << "Initial value of p : " << *(p.load()) << std::endl;

        std::vector<std::thread> threads;
        const std::size_t num_threads = 8;

        for (std::size_t i = 0; i != num_threads; ++i)
        {
                threads.emplace_back([&p, i](){

                        std::shared_ptr<int> x = p.load();
                        while (!p.compare_exchange_weak(
                                x,
                                std::shared_ptr<int>(new int(i + 5))
                        )) ;
                });
        }

        for (auto& t : threads) t.join();

        std::cout << "Use count : " << p.load().use_count() << std::endl;
        std::cout << "Final value of p : " << *(p.load()) << std::endl;
}

编译运行时,输出为:

~$ g++ test2.cpp -o test2 -std=c++11 -lpthread -mcx16
~$ ./test2
Use count : 1
Initial value of p : 0
Use count : 0
Final value of p : 0

但是这个输出对我没有意义。首先,在将原子shared_ptr初始化为值10之后,当我加载它并读取初始值(在生成任何线程之前)时,我得到0。其次,在所有线程加入之后,该值仍为0,即使没有线程可能将其设置为0。最奇怪的是,在线程加入后,shared_ptr的use_count()0!然而,原子shared_ptr对象仍在范围内,因此使用计数应为1

我非常确定GCC的实现存在缺陷,但根据我上面发布的链接,GCC 4.9有一个完整的原子shared_ptr实现,并且......

~$ gcc --version
~$ gcc (Debian 4.9.2-10) 4.9.2

那么......到底发生了什么?我想得到某种确认,即GCC 4.9.2在这里的实现存在缺陷或不完整,或者我对如何使用原子shared_ptr完全错误/困惑。

1 个答案:

答案 0 :(得分:6)

“原子shared_ptr操作”是指std::atomic_(store|load|exchange|compare_exchange_strong|compare_exchange_weak)(_explicit)?的免费shared_ptr函数模板,记录为here。 GCC直到5才有它们。(有趣的是:它的实现实际上使用了全局16个互斥体阵列。)

std::atomic超过std::shared_ptr会导致未定义的行为,因为std::atomic需要一个简单的可复制类型。

并发TS有std::experimental::atomic_shared_ptr