关于weak_ptr的线程安全性

Leo*_*all 15 c++ multithreading weak-ptr c++11

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
}
Run Code Online (Sandbox Code Playgroud)

关于上面的代码,我知道不同的线程读取和写入相同的shared_ptr导致竞争条件.但是怎么样weak_ptr?下面的代码中是否有竞争条件?(我的平台是Microsoft 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)
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*ger 32

我知道我迟到了,但是在搜索"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;
Run Code Online (Sandbox Code Playgroud)

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

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

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

您可以使用从其他线程weak_ptr::lock()获取a shared_ptr而无需进一步同步.这也证实了这里的升压和这个苏答案克里斯小丑,年轻.

同样,你必须确保不修改相同weak_ptr,而从另一个访问它从一个线程,所以传球g_wf3()由价值为好.

  • 您是否注意到OP中的示例涉及一个`shared_ptr`或`weak_ptr`对象,该对象从一个线程读取并在另一个线程中写入而没有同步?由于数据争用,这两个示例都有未定义的行为. (4认同)

Cas*_*sey 5

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


rsj*_*ffe 5

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

  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                   |
+---------------+----------+-------------------------+------------------------+
Run Code Online (Sandbox Code Playgroud)

所述的std cppreference讨论::原子(标准::的weak_ptr)是最明显的上同时访问不同实例的安全性:

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

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