【问题标题】:GCC atomic shared_ptr implementationGCC 原子 shared_ptr 实现
【发布时间】:2016-05-12 22:12:45
【问题描述】:

根据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 实现存在缺陷或不完整,或者我对如何使用 atomic shared_ptr 完全错误/困惑。

【问题讨论】:

  • 我不确定,但似乎可能是后者?在 std::atomic 页面的参考页面上,它没有提到 shared_ptr 的特化:en.cppreference.com/w/cpp/atomic/atomic。实际上,当我尝试编译您的代码时,我触发了一个静态断言:错误:静态断言失败:std::atomic 需要一个可简单复制的类型。所以看起来这是不正确的用法,并且在旧的编译器版本中代码没有针对它进行强化,导致一些奇怪的行为。
  • 这是一个针对 GCC 4.9 的错误报告,已在 5 中修复。
  • 您是否将 atomic 与 lock-free 混淆了?
  • "需要同时原子更新指针本身和引用计数" 通常,修改一个shared_ptr 涉及两个引用计数和一个ptr。而且它们处于不同的内存分配中。

标签: c++ multithreading gcc shared-ptr stdatomic


【解决方案1】:

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

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

并发TS有std::experimental::atomic_shared_ptr

【讨论】:

    猜你喜欢
    • 2016-03-20
    • 2020-11-19
    • 2019-04-18
    • 1970-01-01
    • 2010-10-23
    • 2010-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多