繁体   English   中英

关于weak_ptr的线程安全

[英]About thread-safety of weak_ptr

std::shared_ptr<int> g_s = std::make_shared<int>(1);
void f1()
{
    std::shared_ptr<int>l_s1 = g_s; // read g_s
}

void f2()
{
    std::shared_ptr<int> l_s2 = std::make_shared<int>(3);
    std::thread th(f1);
    th.detach();
    g_s = l_s2; // write g_s
}

关于上面的代码,我知道读取和写入相同shared_ptr的不同线程会导致竞争条件。 但是weak_ptr呢? 下面的代码中是否有任何竞争条件? (我的平台是微软VS2013。)

std::weak_ptr<int> g_w;

void f3()
{
    std::shared_ptr<int>l_s3 = g_w.lock(); //2. here will read g_w
    if (l_s3)
    {
        ;/.....
    }
}

void f4()
{
    std::shared_ptr<int> p_s = std::make_shared<int>(1);
    g_w = p_s;

    std::thread th(f3);
    th.detach();
    // 1. p_s destory will motify g_w (write g_w)
}

我知道我迟到了,但是在搜索“weak_ptr 线程”时会出现这种情况,而 Casey 的回答并不是全部事实。 shared_ptrweak_ptr都可以在线程中使用而无需进一步同步。

对于shared_ptr ,有很多文档(例如在cppreference.comstackoverflow 上)。 您可以安全地访问shared_ptr ,它指向来自不同线程的同一对象。 你不能从两个线程中敲击同一个指针。 换句话说:

// Using p and p_copy from two threads is fine.
// Using p from two threads or p and p_ref from two threads is illegal.
std::shared_ptr<A> p = std::make_shared<A>();
std::shared_ptr<A> &p_ref = p;
std::shared_ptr<A> p_copy = p;

要在您的代码中解决该问题,请将g_s作为参数(按值)* 传递给f1()

对于弱指针,安全保证隐藏在weak_ptr::lock的文档中:

有效地返回expired() ? shared_ptr<T>() : shared_ptr<T>(*this) expired() ? shared_ptr<T>() : shared_ptr<T>(*this) ,以原子方式执行。

您可以使用weak_ptr::lock()从其他线程获取shared_ptr而无需进一步同步。 Boost 和 Chris Jester-Young 的SO 回答中也得到了证实。

同样,您必须确保在从另一个线程访问它时不要从一个线程修改相同的weak_ptr ,因此也将g_w按值传递给f3()

shared_ptrweak_ptr属于与所有其他标准库类型相同的全面线程安全要求:如果成员函数是不可修改的 ( const ),则对成员函数的同时调用必须是线程安全的(详见 C++11 §17.6.5.9 数据竞争避免[res.data.races])。 赋值运算符显然不是const

为了在下面的讨论中简洁起见,所有从相同的原始shared_ptrunique_ptr生成的不同的weak_ptrshared_ptr将被称为“实例”。 在此分析中不需要考虑不共享同一对象的weak_ptrshared_ptr 评估线程安全的一般规则是:

  1. 在同一实例上同时调用const成员函数是线程安全的。 所有观察者函数都是const
  2. 对不同实例的同时调用是线程安全的,即使其中一个调用是修饰符也是如此。
  3. 当至少一个调用是修饰符时,对同一实例的同时调用不是线程安全的。

下表显示了两个线程同时在同一实例上运行时的线程安全性。

+---------------+----------+-------------------------+------------------------+
|   operation   |   type   | other thread modifying  | other thread observing |
+---------------+----------+-------------------------+------------------------+
| (constructor) |          | not applicable          | not applicable         |
| (destructor)  |          | unsafe                  | unsafe                 |
| operator=     | modifier | unsafe                  | unsafe                 |
| reset         | modifier | unsafe                  | unsafe                 |
| swap          | modifier | unsafe                  | unsafe                 |
| use_count     | observer | unsafe                  | safe                   |
| expired       | observer | unsafe                  | safe                   |
| lock          | observer | unsafe                  | safe                   |
| owner_before  | observer | unsafe                  | safe                   |
+---------------+----------+-------------------------+------------------------+

cppreference 对 std::atomic(std::weak_ptr)的讨论在同时访问不同实例的安全性方面最为清晰:

注意 std::weak_ptr 和 std::shared_ptr 使用的控制块是线程安全的:不同的非原子 std::weak_ptr 对象可以使用可变操作(例如 operator= 或 reset)同时被多个线程访问,甚至当这些实例是副本或以其他方式在内部共享相同的控制块时。

C++20 引入了弱指针的std::atomic ,它通过适当的同步提供对同一实例的线程安全修改。 请注意,对于构造函数,从另一个实例初始化不是原子的。 例如, atomic<weak_ptr<T>> myptr(anotherWeakPtr); 不是原子操作。

所以为了让我清除它,我仍然不太确定如果在 std::shared_ptr 上调用 reset(),同时为 std:weak_ptr 调用 lock() 会发生什么。

像这样极其简化:

std::shared_ptr<Object> sharedObject;
std::weak_ptr<Object> weakObject = sharedObject;

void thread1()
{
    std::shared_ptr<Object> leaseObject = weakObject.lock(); //weakObject is bound to sharedObject
}

void thread2()
{
    sharedObject.reset();
}

我们假设 sharedObject 不与任何其他对象共享其指针。

如果这两个命令( reset() 和 lock() )同时发生,我希望:

  • sharedObject 成功重置并且weakObject.lock() 返回nullptr,并且sharedObject 从内存中删除。

  • sharedObject 成功重置并且weakObject.lock() 返回一个指向sharedObject 的指针。 当leaseObject 失去作用域时,sharedObject 将从内存中删除。

如果上面的代码未定义,我必须将我拥有的 Object 类中的 std:mutex 替换到类之外,但这可能是另一个线程中的另一个问题。 ;)

跨线程使用weak_ptr和shared_ptr是安全的; weak_ptr/shared_ptr 对象本身不是线程安全的。 您不能跨线程读取/写入单个智能指针。

像您一样访问 g_s 并不安全,无论是 shared_ptr 还是weak_ptr。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM