C++ 使用指向 std::shared_ptr 的原始指针
C++ Using Raw pointer to std::shared_ptr
所以我试图通过我正在使用的消息传递机制在线程之间传递共享指针。由于 serialization/deserialization 的工作原理,我无法直接将 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 在同一个 process/host 中。我将 shared_ptr 放入由 thread1 管理的地图中(我试图省略我最初认为不重要的细节)。然而,我没有意识到的是,我从所述地图中检索的方式是不正确的。我正在将映射的内容复制到临时文件 shared_ptr 中,然后将临时文件的地址 shared_ptr 发送到线程 2。所以我无意中发送了堆栈上变量的地址。愚蠢的错误,但我认为该线程中的评论仍然相当 instructive/helpful。以下似乎解决了我的问题。
auto& objPtr = m_objMap[index];
uint64_t serializedPtr = reinterpret_cast<uint64_t>(&objPtr);
shared_ptr
在您复制它时(使用赋值 operator=
)自动增加和减少其内部存储的引用计数,并且 - 重要的是 - 当 shared_ptr
被销毁时(由超出范围)。您传输指向共享指针的指针的方法在上面的代码中存在根本性缺陷,因为您传输的是临时共享指针的 地址 ,而不是 所有权或寿命。 shared_ptr
- 仍然由线程 A 拥有 - 可能会超出范围并在线程 B 可以使用它之前被销毁。
为了转让 shared_ptr 实例的所有权,我建议创建一个 heap-allocated/dynamic shared_ptr
用于转让。这可以使用 new
或(甚至更好)make_unique
来完成。使用 unique_ptr(即:unique_ptr<shared_ptr<ObjectClass>>
),您将使用 'release' 方法,然后将指针传递到消息中的线程屏障。
线程 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;
您可以通过简单地 'new'-ing 原始 shared_ptr
而不是将其捕获在 unique_ptr<shared_ptr<T>>
中来进一步缩短它,但我个人更喜欢这种方法,因为它具有清晰的 "capture" 和 "release" 飞行指针的语义。
您的代码将临时共享指针的地址从一个线程发送到另一个线程。您在堆栈上创建 objPtr
作为对象,并将 objPtr
的地址(不是对象的地址!)传递给线程。
那不是你想做的。你有两个理智的选择:
将对象的地址从一个线程发送到另一个线程并确保始终存在至少一个指向该对象的共享指针(如您声称您的代码已经这样做了)。这是一个可怕的修复,因为它不能确保对象的生命周期是足够的。
使用new
为对象动态分配一个额外的共享指针,并将指向该共享指针的指针传递给线程。在对象完成后,让线程 delete
成为共享指针。这可能是确保对象的生命周期足够的最简单的修复。
将对象传递给线程的一般模式是创建一个 class 或结构来携带要传递给线程的所有信息。在创建线程之前,使用 new
创建结构的新实例并根据需要进行填充。然后将该结构的地址传递给线程。当线程完成结构时,让线程使用 delete
释放结构。您可以使用它来将任何对象或数据集合传递给线程。在这种情况下,您想将实际的 shared_ptr
传递给线程——而不是一个的地址。
所以我试图通过我正在使用的消息传递机制在线程之间传递共享指针。由于 serialization/deserialization 的工作原理,我无法直接将 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 在同一个 process/host 中。我将 shared_ptr 放入由 thread1 管理的地图中(我试图省略我最初认为不重要的细节)。然而,我没有意识到的是,我从所述地图中检索的方式是不正确的。我正在将映射的内容复制到临时文件 shared_ptr 中,然后将临时文件的地址 shared_ptr 发送到线程 2。所以我无意中发送了堆栈上变量的地址。愚蠢的错误,但我认为该线程中的评论仍然相当 instructive/helpful。以下似乎解决了我的问题。
auto& objPtr = m_objMap[index];
uint64_t serializedPtr = reinterpret_cast<uint64_t>(&objPtr);
shared_ptr
在您复制它时(使用赋值 operator=
)自动增加和减少其内部存储的引用计数,并且 - 重要的是 - 当 shared_ptr
被销毁时(由超出范围)。您传输指向共享指针的指针的方法在上面的代码中存在根本性缺陷,因为您传输的是临时共享指针的 地址 ,而不是 所有权或寿命。 shared_ptr
- 仍然由线程 A 拥有 - 可能会超出范围并在线程 B 可以使用它之前被销毁。
为了转让 shared_ptr 实例的所有权,我建议创建一个 heap-allocated/dynamic shared_ptr
用于转让。这可以使用 new
或(甚至更好)make_unique
来完成。使用 unique_ptr(即:unique_ptr<shared_ptr<ObjectClass>>
),您将使用 'release' 方法,然后将指针传递到消息中的线程屏障。
线程 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;
您可以通过简单地 'new'-ing 原始 shared_ptr
而不是将其捕获在 unique_ptr<shared_ptr<T>>
中来进一步缩短它,但我个人更喜欢这种方法,因为它具有清晰的 "capture" 和 "release" 飞行指针的语义。
您的代码将临时共享指针的地址从一个线程发送到另一个线程。您在堆栈上创建 objPtr
作为对象,并将 objPtr
的地址(不是对象的地址!)传递给线程。
那不是你想做的。你有两个理智的选择:
将对象的地址从一个线程发送到另一个线程并确保始终存在至少一个指向该对象的共享指针(如您声称您的代码已经这样做了)。这是一个可怕的修复,因为它不能确保对象的生命周期是足够的。
使用
new
为对象动态分配一个额外的共享指针,并将指向该共享指针的指针传递给线程。在对象完成后,让线程delete
成为共享指针。这可能是确保对象的生命周期足够的最简单的修复。
将对象传递给线程的一般模式是创建一个 class 或结构来携带要传递给线程的所有信息。在创建线程之前,使用 new
创建结构的新实例并根据需要进行填充。然后将该结构的地址传递给线程。当线程完成结构时,让线程使用 delete
释放结构。您可以使用它来将任何对象或数据集合传递给线程。在这种情况下,您想将实际的 shared_ptr
传递给线程——而不是一个的地址。