21
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)
}
4

5 回答 5

40

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

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

// 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),原子执行。

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

同样,您必须确保在weak_ptr从另一个线程访问它时不要从一个线程修改相同的内容,因此g_wf3()按值传递。

于 2015-05-06T18:56:12.090 回答
7

为了在下面的讨论中简洁起见,不同weak_ptr的 s 和shared_ptrs 都从相同的原始生成shared_ptrunique_ptr将被称为“实例”。weak_ptr不共享同一对象的 s 和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                   |
+---------------+----------+-------------------------+------------------------+

std::atomic(std::weak_ptr)的cppreference 讨论对同时访问不同实例的安全性最清楚:

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

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

于 2018-07-28T19:16:04.927 回答
5

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

于 2013-12-20T15:18:47.130 回答
0

所以为了给我清除它,我仍然不太确定如果在 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 替换到类之外,但这可能是另一个线程中的另一个问题。;)

于 2019-11-22T13:16:14.850 回答
0

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

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

于 2022-01-19T23:03:46.597 回答