代码之家  ›  专栏  ›  技术社区  ›  Vincent Van Groot

访问shared\u ptr持有的类的原子成员

  •  3
  • Vincent Van Groot  · 技术社区  · 7 年前

    我正在尝试创建一个小类,它将允许我促进两个线程之间的通信。

    当这些线程在线程池中排队时,它们很可能会超过创建上述类的上下文。

    到目前为止我所尝试的( on coliru as well ):

    class A    
    {
    public:
        A(int maxVal) : maxValue(maxVal) {}    
        bool IsOverMax() const { return cur >= maxValue; }    
        void Increase() { cur++; }
    
    private:
        const int  maxValue;    
        atomic_int cur{ 0 };
    };
    

    可能的用途:

    void checking(const shared_ptr<A> counter)
    {
        while(!counter->IsOverMax())
        {
            cout<<"Working\n";         // do work
            std::this_thread::sleep_for(10ms);
        }
    }
    
    void counting(shared_ptr<A> counter)
    {
        while (!counter->IsOverMax())
        {
            cout<<"Counting\n";
            counter->Increase(); // does this fall under `...uses a non-const member function of shared_ptr then a data race will occur`?  http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
            std::this_thread::sleep_for(9ms);
        }
    }
    
    int main() 
    { 
    
        unique_ptr<thread> t1Ptr;
        unique_ptr<thread> t2Ptr;
    
        {
            auto aPtr = make_shared<A>(100); // This might be out of scope before t1 and t2 end
            t1Ptr.reset(new thread(checking, aPtr)); // To simbolize that t1,t2 will outlive the scope in which aPtr was originaly created
            t2Ptr.reset(new thread(counting, aPtr));
        }
    
        t2Ptr->join();
        t1Ptr->join();
        //cout<< aPtr->IsOverMax();
    }
    

    我担心的原因是 documentation 说:

    如果多个执行线程访问同一个std::shared\u ptr对象而不进行同步,并且其中任何一个访问都使用 shared\u ptr的非常量成员函数,则将发生数据竞争 除非所有此类访问都是通过这些函数执行的,这些函数是相应原子访问函数的重载(std::atomic\u load、std::atomic\u store等)

    • 所以 Increase 是一个非常量函数,aPtr的副本是 the same std::shared_ptr 是否适用于这种情况?
    • 这段代码是线程安全的吗?
    • 对于非原子对象(比如使用std::mutex锁定对常规int的读写),这可以吗?
    • 无论如何,为什么?
    2 回复  |  直到 7 年前
        1
  •  2
  •   LWimsey    7 年前

    因此,Increase是一个非常量函数,aPtr的副本是否与此上下文中的std::shared\u ptr相同?

    std::thread 创造 aPtr 按值传递。因此,保证:

    • 您不会引入数据竞争,因为每个线程都有自己的 shared_ptr (尽管它们管理相同的对象 A ).
      您所参考的文档描述了一个场景,其中多个线程在同一个线程上操作 共享\u ptr 例子 在这种情况下,只能调用常量成员函数(见下文),或者需要同步。
    • 共享\u ptr 引用计数在之前递增 aPtr 超出范围 main

    是的,这是一种正确的使用方法 共享\u ptr .

    这段代码是线程安全的吗?

    您的代码没有引入数据竞争,也没有访问 共享\u ptr 实例,也不具有对托管对象的访问权限 A. . 这意味着多个线程不会对同一内存位置执行冲突的、非原子的读写操作。

    但是,请记住 checking() ,调用 IsOverMax() 与接下来的实际工作分离 ( Increase() 之后的第二个线程可以调用 IsOverMax() 但在' 开展工作 '). 因此,你可以 开展工作 '当 cur 已超过最大值。 这是否是一个问题取决于您的规范,但它被称为竞争条件,它不一定是编程错误(与导致未定义行为的数据竞争不同)。

    对于非原子对象(比如使用std::mutex锁定对常规int的读写),这可以吗?

    电流 可以是普通的 int (非原子)如果你用 std::mutex . 必须为两次写入锁定互斥锁 读取访问以防止数据竞争。

    打电话时的一句话 const 多线程共享的对象上的成员函数。
    使用 常量 单独使用并不能保证不会引入数据竞争。
    在这种情况下,担保适用于 共享\u ptr const成员函数,因为文档中这么说。
    我在 C++标准 该保证是否适用于 标准库

        2
  •  0
  •   Nevin    7 年前

    该文档讨论的是 shared_ptr ,而不是类的成员函数。副本 共享\u ptr 对象是不同的对象。

    我相信代码是线程安全的,因为在不同线程上写入和读取的唯一变化变量是 cur ,该变量是原子变量。

    如果 电流 不是原子的,并且在这两个方面都可以访问它 Increase() IsOverMax() 通过锁定 std::mutex ,该代码也将是线程安全的。