gpt4 book ai didi

c++ - std::shared_ptr 复制构造函数线程安全

转载 作者:行者123 更新时间:2023-11-30 01:35:22 25 4
gpt4 key购买 nike

std::shared_ptr 规范保证只有一个线程会在内部指针上调用 delete。这answer对 shared_ptr 引用计数操作所需的内存排序有一个非常好的解释,以保证删除将在同步内存上调用。

我不明白的是:

  • 如果一个 shared_ptr 被一个复制构造函数初始化,它是保证它将为空或有效的 shared_ptr?

我正在查看 shared_ptr 复制构造函数的 MVCC 实现。而且我想我至少可以确定一种竞争条件。

template<class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other)
{ // implement shared_ptr's (converting) copy ctor
if (_Other._Rep)
{
_Other._Rep->_Incref();
}

_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}

实现检查控制 block 是否有效,然后增加其引用计数,并复制分配内部字段。

假设 _Other 属于不同的线程,然后是调用复制构造函数的线程。如果在 if (_Other._Rep)_Other._Rep->_Incref(); 行之间,此线程调用碰巧删除控制 block 和指针的析构函数,然后 _Other._Rep->_Incref() 将取消引用已删除的指针。

进一步说明

这是一个代码,说明了我正在谈论的极端情况。我将调整 share_ptr 复制构造函数实现以模拟上下文切换:

template<class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other)
{ // implement shared_ptr's (converting) copy ctor
if (_Other._Rep)
{
// now lets put here a really long loop or sleep to simulate a context switch
int count = 0;
for (int i = 0; i < 99999999; ++i)
{
for (int j = 0; j < 99999999; ++j)
{
count++;
}
}

// by the time we get here, the owning thread may already destroy the shared_ptr that was passed to this constructor
_Other._Rep->_Incref();
}

_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}

下面是一段可能会显示问题的代码:

int main()
{
{
std::shared_ptr<int> sh1 = std::make_shared<int>(123);
auto lambda = [&]()
{
auto sh2 = sh1;
std::cout << sh2.use_count(); // this prints garbage, -572662306 in my case
};

std::thread t1(lambda);
t1.detach();
// main thread destroys the shared_ptr
// background thread probably did not yet finished executing the copy constructor
}



Sleep(10000);

}

最佳答案

shared_ptr 使用正确时,您描述的情况永远不会发生。

被复制的 shared_ptr 在被传递给复制构造函数之前增加了引用计数,并且在复制构造函数退出之前它不能被破坏,因为它是构造函数。

因此,另一个线程不会销毁正在共享的对象。如果 _Other.Rep 不为空,则 _Other.Rep 的引用计数在进入复制构造函数时始终至少为 1。

更新:您的用例有问题。 lambda 捕获了一个对主线程的 shared_ptr 实例的 reference,但是该线程直到 shared_ptr 已经退出后才复制该 shared_ptr范围并被 main 销毁。您的线程有一个悬挂引用,导致您的代码有未定义的行为。这不是 shared_ptr 实现的错误。您的 lambda 需要捕获 shared_ptr by value 而不是 by reference ,因此它的 refcount 在创建线程之前立即递增,而不是当线程开始运行时。

关于c++ - std::shared_ptr 复制构造函数线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54279859/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com