首页 > 解决方案 > C++ 使用指向 std::shared_ptr 的原始指针

问题描述

所以我试图通过我正在使用的消息传递机制在线程之间传递一个共享指针。由于序列化/反序列化的工作方式,我无法将 shared_ptr 直接嵌入到我发送的消息中。所以我实际上需要发送一个 shared_ptr 的原始指针。见下文:

线程 1:

auto objPtr = std::make_shared<ObjectClass>();
uint64_t serializedPtr = reinterpret_cast<uint64_t>(&objPtr);

线程 2:

std::shared_ptr<ObjectClass> objPtrT2 = *(reinterpret_cast<std::shared_ptr<ObjectClass>*>(serializedPtr));

当线程 2 增加共享指针的引用计数时,这有时会崩溃。我只能假设这里有一些竞争条件,但无法找出解决方案。请注意,它并不总是崩溃,反序列化似乎总是成功的。

我是否需要同步对此 shared_ptr 的访问(shared_ptr 本身,而不是 share_ptr 指向的内容)?我担心我传输此 shared_ptr 的方式会破坏引用计数的管理方式。

出于其他与性能相关的原因,我仍在争论在此处使用 shared_ptr 是否合适,但我想知道为了自己的利益我做错了什么。

谢谢

编辑: 请注意,线程 1 和线程 2 位于同一进程/主机中。我将 shared_ptr 放置到由 thread1 管理的地图中(我试图省略我最初认为不重要的细节)。然而,我没有意识到的是,我从所述地图中检索的方式是不正确的。我将地图的内容复制到临时 shared_ptr 中,然后将临时 shared_ptr 的地址发送到 thread2。所以我无意中发送了堆栈上变量的地址。愚蠢的错误,但我认为这个线程中的评论仍然很有启发性/有帮助。以下似乎解决了我的问题。

auto& objPtr = m_objMap[index];
uint64_t serializedPtr = reinterpret_cast<uint64_t>(&objPtr);

标签: c++multithreadingthread-safetyshared-ptrrace-condition

解决方案


shared_ptr当您复制它时(使用 assignment )自动增加和减少其内部存储的引用计数operator=,并且 - 重要的是 - 当 ashared_ptr被销毁时(通过超出范围)。您传输指向共享指针的指针的方法在上面的代码中存在根本缺陷,因为您传输的是临时共享指针的地址,而不是所有权或 lifespanshared_ptr仍然由线程 A 拥有的 - 可能会超出范围并在线程 B 可以使用它之前被销毁 。

为了转移 shared_ptr 实例的所有权,我建议创建一个堆分配/动态shared_ptr的转移。这可以使用newor (甚至更好)来完成make_unique。使用 unique_ptr (ie: unique_ptr<shared_ptr<ObjectClass>>),在将指针传递到消息中的线程屏障之前,您将使用“释放”方法。

线程 A:

auto sharedPtr = std::make_shared<ObjectClass>();

// This line creates a heap-allocated copy of a 
// shared_ptr (incrementing reference count)
// And places ownership inside a unique_ptr
auto sharedPtrDynamicCopy = std::make_unique<decltype(sharedPtr)>(sharedPtr);

// This 'releases' ownership of the heap-allocated shared_ptr,
// returning a raw pointer; it is now a potential
// memory leak!!!  It must be 'captured' in Thread B.
auto rawPtrToPass = sharedPtrDynamicCopy.release();

线程 B:

// Here, we immediately 'capture' the raw pointer back
// inside a unique_ptr, closing the loop on the potential
// memory leak
auto sharedPtrDynamicCopy = unique_ptr<shared_ptr<ObjectClass>>(rawPtrFromThreadA);

// Now we can make a copy of the shared_ptr, if we like.
// This sharedCopy will live on, even after recvdPtr goes
// out of scope.
auto sharedCopy = *sharedPtrDynamicCopy;

您也许可以通过简单地“新建”原始shared_ptr而不是在 a 中捕获它来进一步缩短此时间unique_ptr<shared_ptr<T>>,但我个人更喜欢这种方法,因为它对飞行中的指针具有明确的“捕获”和“释放”语义。


推荐阅读