在 C++ 多线程中,什么时候应该将互斥体与 std::shared_ptr 一起使用?

Sam*_*ami 6 c++ multithreading shared-ptr

std::shared_ptr<Dog> pd;

void F() {
    pd = std::make_shared<Dog>("Smokey");
}

int main() {
    std::thread t1(F);
    std::thread t2(F);
    t1.join();
    t2.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)
std::shared_ptr<Dog> pd(new Dog("Gunner"));

void F() {
    std::shared_ptr<Dog> localCopy = pd;
}

int main() {
    std::thread t1(F);
    std::thread t2(F);
    t1.join();
    t2.join();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 C++ 中,我知道 std::shared_ptr 对于读取和复制来说是线程安全的。但我对何时需要使用互斥体来同步线程有点困惑。我有两个代码片段。在第一个中,std::shared_ptr 被多个线程修改。在第二个线程中,每个线程仅读取和复制共享指针。在这两种情况下我都需要互斥体,还是只在第一种情况下需要互斥体?为什么或者为什么不?”

Sam*_*hik 9

如果您仔细识别所涉及的所有移动部分,就更容易理解这一点:

  • 一个引用计数对象,这是一个对象,在某个地方,这是你的Dog

  • 引用计数器本身,它跟踪对引用计数对象的引用数量

  • 使用引用计数器的共享指针本身

这些都是离散的实体,需要单独考虑和评估。

根据经验,在 C++ 中,如果从多个执行线程访问一个对象,并且至少一个执行线程以某种方式“修改”它,则执行线程必须相对于该对象“同步”;除非该对象是“线程安全的”。“同步”是什么意思?嗯,它不仅仅意味着某个地方的互斥锁;它还意味着一个互斥体。但对于这个实际示例来说,这就是它的含义:您需要在某个地方的某个互斥锁上持有锁的同时访问该对象。

数据点:引用计数器是线程安全的。智能指针,又名std::shared_ptr不是。

pd = std::make_shared<Dog>("Smokey");
Run Code Online (Sandbox Code Playgroud)

这会修改多个执行线程中的共享指针pd。这需要同步,这不是线程安全的。你需要一个互斥体。

std::shared_ptr<Dog> localCopy = pd;
Run Code Online (Sandbox Code Playgroud)

这会复制 的副本pd,但不会对其进行修改。作为复制(和销毁)参考计数器的一部分,这也会影响参考计数器。引用计数器是线程安全的。共享指针未被修改,仅被访问。这是线程安全的。

  • 您根本不需要“std::mutex”来与“std::shared_ptr”交互。有 `std::atomic&lt;std::shared_ptr&gt;`。仅当您同时修改“Dog”时,互斥锁才变得必要。 (2认同)