shared_ptr 销毁时的线程安全性

问题描述 投票:0回答:1

对于给定的程序:

#include <iostream>
#include <memory>
#include <thread>

void threadFunction(std::shared_ptr<int> ptr) {
    std::cout << "Worker thread: " << *ptr << std::endl;
}

int main() {
    auto sharedInt = std::make_shared<int>(42);

    std::cout << "Main thread: " << *sharedInt << std::endl;

    std::thread t(threadFunction, sharedInt);

    sharedInt.reset();

    t.join();

    return 0;
}

我遇到 valgrind(helgrind 工具)错误:

 
==12797== Possible data race during write of size 4 at 0x4E38088 by thread #1
==12797== Locks held: none
==12797==    at 0x10A4B9: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:343)
==12797==    by 0x10A794: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:1071)
==12797==    by 0x10A663: std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:1524)
==12797==    by 0x10A8EF: std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::reset() (shared_ptr_base.h:1642)
==12797==    by 0x10A300: main (shared_ptr.cpp:17)
==12797== 
==12797== This conflicts with a previous read of size 4 by thread #2
==12797== Locks held: none
==12797==    at 0x10A49E: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:337)
==12797==    by 0x10A794: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:1071)
==12797==    by 0x10A663: std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:1524)
==12797==    by 0x10A67F: std::shared_ptr<int>::~shared_ptr() (shared_ptr.h:175)
==12797==    by 0x10BB2D: void std::__invoke_impl<void, void (*)(std::shared_ptr<int>), std::shared_ptr<int> >(std::__invoke_other, void (*&&)(std::shared_ptr<int>), std::shared_ptr<int>&&) (invoke.h:61)
==12797==    by 0x10BA74: std::__invoke_result<void (*)(std::shared_ptr<int>), std::shared_ptr<int> >::type std::__invoke<void (*)(std::shared_ptr<int>), std::shared_ptr<int> >(void (*&&)(std::shared_ptr<int>), std::shared_ptr<int>&&) (invoke.h:96)
==12797==    by 0x10B9E4: void std::thread::_Invoker<std::tuple<void (*)(std::shared_ptr<int>), std::shared_ptr<int> > >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) (std_thread.h:292)
==12797==    by 0x10B985: std::thread::_Invoker<std::tuple<void (*)(std::shared_ptr<int>), std::shared_ptr<int> > >::operator()() (std_thread.h:299)
==12797==  Address 0x4e38088 is 8 bytes inside a block of size 24 alloc'd
==12797==    at 0x4846023: operator new(unsigned long) (vg_replace_malloc.c:483)
==12797==    by 0x10B51E: std::__new_allocator<std::_Sp_counted_ptr_inplace<int, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (new_allocator.h:147)
==12797==    by 0x10B10F: allocate (alloc_traits.h:482)
==12797==    by 0x10B10F: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<int, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<int, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<int, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&) (allocated_ptr.h:98)
==12797==    by 0x10AF48: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<int, std::allocator<void>, int>(int*&, std::_Sp_alloc_shared_tag<std::allocator<void> >, int&&) (shared_ptr_base.h:969)
==12797==    by 0x10AD29: std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>, int>(std::_Sp_alloc_shared_tag<std::allocator<void> >, int&&) (shared_ptr_base.h:1712)
==12797==    by 0x10AA60: std::shared_ptr<int>::shared_ptr<std::allocator<void>, int>(std::_Sp_alloc_shared_tag<std::allocator<void> >, int&&) (shared_ptr.h:464)
==12797==    by 0x10A753: std::shared_ptr<std::enable_if<!std::is_array<int>::value, int>::type> std::make_shared<int, int>(int&&) (shared_ptr.h:1010)
==12797==    by 0x10A294: main (shared_ptr.cpp:11)
==12797==  Block was alloc'd by thread #1

17号线在哪里

sharedInt.reset();

根据我的理解,这个程序应该完全没问题,因为两个线程都在

shared_ptr
对象的不同实例上运行。

我说得对吗?

c++ multithreading thread-safety shared-ptr
1个回答
0
投票

你的主线程中有一个shared_ptr,

sharedInt
,它在创建新线程时被复制。

copy
操作是对
shared_ptr
本身的操作。虽然shared_ptrs引用计数器是线程安全的,因此您可以同时安全地销毁多个shared_ptr(指向同一个ptr),但这并不意味着单个
shared_ptr
本身不会出现数据争用。

在这种情况下,您正在尝试同时

copy
reset()
sharedInt
,这是不安全的。请记住,
reset()
不仅会修改引用计数,还会将 ptr 更改为 null。

如果您确实需要在主线程中销毁

sharedInt
,则需要一种方法从第二个线程向主线程发出信号,表明您已完成复制。您可以考虑轮询shared_ptr上的
use_count()
,直到它增加后再重置。

std::this_thread::yield(); // hope this gets the other thread to run

while (sharedInt.use_count() < 2)
    std::this_thread::sleep_for(std::chrono::milliseconds(1));

sharedInt.reset();
© www.soinside.com 2019 - 2024. All rights reserved.