C++ 关于弱线程ptr的线程安全性

C++ 关于弱线程ptr的线程安全性,c++,multithreading,c++11,weak-ptr,C++,Multithreading,C++11,Weak Ptr,shared_ptr和weak_ptr与所有其他标准库类型属于相同的总体线程安全要求:如果成员函数是非修改的(const)(详见C++11§17.6.5.9数据争用避免[res.Data.races]),则对成员函数的同时调用必须是线程安全的。赋值运算符显然不是常量,我知道我迟到了,但这是在搜索“弱线程”时出现的,Casey的答案并不完全正确无需进一步同步,即可从线程使用shared_ptr和weak_ptr。 对于共享\u ptr,有很多文档(例如on或on)。您可以从不同线程安全地访问指向同

shared_ptr
weak_ptr
与所有其他标准库类型属于相同的总体线程安全要求:如果成员函数是非修改的(
const
)(详见C++11§17.6.5.9数据争用避免[res.Data.races]),则对成员函数的同时调用必须是线程安全的。赋值运算符显然不是常量,我知道我迟到了,但这是在搜索“弱线程”时出现的,Casey的答案并不完全正确无需进一步同步,即可从线程使用
shared_ptr
weak_ptr

对于
共享\u ptr
,有很多文档(例如on或on)。您可以从不同线程安全地访问指向同一对象的
共享\u ptr
。你就是不能从两个线程中敲打同一个指针。换言之:

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)
}
//从两个线程使用p和p_拷贝是可以的。
//从两个线程使用p或从两个线程使用p和p_ref是非法的。
标准::共享\u ptr:

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

您可以使用
弱\u ptr::lock()
从其他线程获取
共享的\u ptr
,而无需进一步同步。克里斯·杰斯特·杨也证实了这一点


同样,在从另一个线程访问时,必须确保不要从一个线程修改相同的
weak\u ptr
,因此也要通过值将
g\u w
传递到
f3()

为了在下面的讨论中简洁起见,从同一原始
共享ptr
唯一ptr
生成的不同
弱ptr
共享ptr
将被称为“实例”<代码>弱\u ptr
s和不共享同一对象的
共享\u ptr
s在本分析中不需要考虑。评估螺纹安全性的一般规则如下:

  • 在同一实例上同时调用
    const
    成员函数是线程安全的。所有观察者函数都是常量
  • 对不同实例的同时调用是线程安全的,即使其中一个调用是修饰符
  • 当至少有一个调用是修饰符时,对同一实例的同时调用不是线程安全的
  • 下表显示了两个线程同时在同一实例上运行时的线程安全性

    // 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;
    
    最清楚的是同时访问不同实例的安全性:

    请注意,std::weak_ptr和std::shared_ptr使用的控制块是 线程安全:可以使用 可变操作,如运算符=或重置,同时由多个 线程,即使这些实例是副本或共享相同的实例 内部控制块


    C++20引入了弱指针的
    std::atomic
    专门化,它通过适当的同步对同一实例进行线程安全的修改。注意,对于构造函数,来自另一个实例的初始化不是原子的。例如,
    atomicmyptr(另一个弱点)
    不是一个原子操作。

    所以为了给我澄清这一点,我仍然不太确定如果对std::shared\u ptr调用reset(),同时对std:weak\u ptr调用lock()会发生什么

    非常简单,如下所示:

    +---------------+----------+-------------------------+------------------------+
    |   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::shared_ptr sharedObject;
    std::weak_ptr weakObject=共享对象;
    void thread1()
    {
    std::shared_ptr leaseObject=weakObject.lock();//weakObject绑定到sharedObject
    }
    无效线程2()
    {
    reset();
    }
    
    我们假设sharedObject不与任何其他对象共享其指针

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

    • sharedObject已成功重置,weakObject.lock()返回null ptr,sharedObject将从内存中删除

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


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

    是的,l_s3是否为空PTR完全是任意的。处理器的内核越多,f3()越有可能提前开始运行,因此为nullptr的可能性越小。这是一个标准的线程争用bug,与弱的ptr实现完全无关。实际上比这更糟糕:具有数据争用的程序具有未定义的行为。在典型的实现中,读取
    f3
    中的
    g_w
    将看到值
    nullptr
    p_s
    ——但该标准不保证该行为。特别是,正常的单字写入是非原子的实现可能会返回一个“撕裂”值,即旧值和部分新值。一个弱指针可能是空的,如果你足够快去抓取它,那么对你来说是好的。如果不是,那就太糟糕了,但这不会在软件中引起错误。使用弱引用时,需要确保两条路径(锁定成功和失败)都是有效路径。如果你在任何一种情况下都有一个bug,它与比赛条件无关,它只是一个缺陷。如果您需要随时锁定,请将其共享。
    f3()
    f4()
    的可见代码很好。但是,注释后的代码不是很好:
    //1。p_s destroy将修改g_w(写入g_w)
    。在
    f4()
    中更新
    g_w
    ,同时通过
    g_w.lock()
    f3()
    中读取
    g_w
    是未定义的行为!
    std::shared\u ptr上有更强的线程安全保证
    
    +---------------+----------+-------------------------+------------------------+
    |   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::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();
    }