根据C++ documentation,shared_ptr的控制块是线程安全的。即,无需显式锁定,多个线程可以访问operator =或reset 但我看到一种奇怪的行为;共享对象偶尔会被双重释放:
#include <iostream>
#include <memory>
#include <unistd.h>
using namespace std;
class MyClass {
public:
MyClass(string x) : name(x) {cout<<"C->"<<name<<endl;};
~MyClass() {cerr<<"D->"<<name<<endl;};
string name;
};
shared_ptr<MyClass> a;
void* tfunc(void*) {
while(1){
{
shared_ptr<MyClass> s = a;
usleep(5);
}
}
}
int main(void) {
pthread_t threadid;
a = make_shared<MyClass>("a");
if(pthread_create(&threadid, NULL, tfunc, NULL)) {
cout<<"pthread_create error"<<endl;
return -1;
}
while(1){
usleep(5);
a = make_shared<MyClass>("b");
}
pthread_join(threadid, NULL);
return 0;
}
这是Address Sanitizer输出:
==28588==ERROR: AddressSanitizer: heap-use-after-free on address 0xb33a7ad4 at pc 0x080490c4 bp 0xb54ff1d8 sp 0xb54ff1c8
WRITE of size 4 at 0xb33a7ad4 thread T1
#0 0x80490c3 in __exchange_and_add /usr/include/c++/6/ext/atomicity.h:49
#1 0x80491ed in __exchange_and_add_dispatch /usr/include/c++/6/ext/atomicity.h:82
#2 0x8049a9e in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/6/bits/shared_ptr_base.h:147
#3 0x80498a3 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/6/bits/shared_ptr_base.h:662
#4 0x804977e in std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/6/bits/shared_ptr_base.h:928
#5 0x8049797 in std::shared_ptr<MyClass>::~shared_ptr() /usr/include/c++/6/bits/shared_ptr.h:93
#6 0x80492d7 in tfunc(void*) /tmp/main.cpp:19
#7 0xb7248c4e (/usr/lib/i386-linux-gnu/libasan.so.3+0x26c4e)
#8 0xb6ea5304 in start_thread (/lib/i386-linux-gnu/libpthread.so.0+0x6304)
#9 0xb6fb347d in __clone (/lib/i386-linux-gnu/libc.so.6+0xe947d)
0xb33a7ad4 is located 4 bytes inside of 36-byte region [0xb33a7ad0,0xb33a7af4)
freed by thread T0 here:
#0 0xb72e7174 in operator delete(void*) (/usr/lib/i386-linux-gnu/libasan.so.3+0xc5174)
#1 0x804ace0 in __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2> >::deallocate(std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2>*, unsigned int) /usr/include/c++/6/ext/new_allocator.h:110
#2 0x804ab07 in std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2> > >::deallocate(std::allocator<std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2> >&, std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2>*, unsigned int) /usr/include/c++/6/bits/alloc_traits.h:442
#3 0x804a818 in std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2> > >::~__allocated_ptr() /usr/include/c++/6/bits/allocated_ptr.h:73
#4 0x804b0aa in std::_Sp_counted_ptr_inplace<MyClass, std::allocator<MyClass>, (__gnu_cxx::_Lock_policy)2>::_M_destroy() /usr/include/c++/6/bits/shared_ptr_base.h:537
#5 0x8049bbe in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/6/bits/shared_ptr_base.h:166
#6 0x80498a3 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/6/bits/shared_ptr_base.h:662
#7 0x804977e in std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/6/bits/shared_ptr_base.h:928
#8 0x8049cd4 in std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<MyClass, (__gnu_cxx::_Lock_policy)2>&&) /usr/include/c++/6/bits/shared_ptr_base.h:1003
#9 0x8049a7c in std::shared_ptr<MyClass>::operator=(std::shared_ptr<MyClass>&&) /usr/include/c++/6/bits/shared_ptr.h:294
#10 0x8049430 in main /tmp/main.cpp:35
#11 0xb6ee2275 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18275)
GCC-6.2和LLVM-3.9显示相同的行为。这是C ++库中的错误吗?
答案 0 :(得分:3)
没有。 =
和reset
不是线程安全的。 <{1}}函数的shared_ptr
重载是必需的。
&#34;控制块是线程安全的&#34;表示您可以在多个线程中使用std::atomic_...
和=
(但每个线程使用单独的reset
),即使所有shared_ptr
个对象都是彼此的副本< /强>