GCC 原子 shared_ptr 实现
GCC atomic shared_ptr implementation
根据 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57250,GCC 4.9 支持原子 shared_ptr
操作。
使用 GCC 4.9.2,我能够编译一个使用 atomic 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 实现是有缺陷的,但是根据我在上面发布的 link,GCC 4.9 有一个完整的原子 shared_ptr
实现,并且...
~$ gcc --version
~$ gcc (Debian 4.9.2-10) 4.9.2
所以...这里到底发生了什么?我想得到某种确认,即这里的 GCC 4.9.2 实现存在缺陷或不完整,或者我完全 wrong/confused 关于如何使用 atomic shared_ptr
.
"atomic shared_ptr
operations" 指的是 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
.
根据 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57250,GCC 4.9 支持原子 shared_ptr
操作。
使用 GCC 4.9.2,我能够编译一个使用 atomic 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 实现是有缺陷的,但是根据我在上面发布的 link,GCC 4.9 有一个完整的原子 shared_ptr
实现,并且...
~$ gcc --version
~$ gcc (Debian 4.9.2-10) 4.9.2
所以...这里到底发生了什么?我想得到某种确认,即这里的 GCC 4.9.2 实现存在缺陷或不完整,或者我完全 wrong/confused 关于如何使用 atomic shared_ptr
.
"atomic shared_ptr
operations" 指的是 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
.