访问shared_ptr

时间:2017-12-28 16:34:32

标签: multithreading c++11 thread-safety atomicity

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

这些线程最有可能比上面提到的类的上下文更长,因为它们排队到线程池上。

到目前为止我尝试了什么(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_ptr对象,并且这些访问中的任何一个使用shared_ptr的非const成员函数,那么将发生数据竞争,除非所有这样的访问都是通过这些函数执行,这些函数是相应的原子访问函数(std :: atomic_load,std :: atomic_store等)的重载

  • 所以Increase是一个非const函数,对于这个上下文,aPtr的副本是the same std::shared_ptr吗?
  • 此代码是否是线程安全的?
  • 对于非原子对象(例如使用std :: mutex锁定对常规int的读写操作)会这样吗?
  • 无论如何?

2 个答案:

答案 0 :(得分:2)

  

所以Increase是一个非const函数,aPtr的副本是否与此上下文的std :: shared_ptr相同?

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

  • 您没有引入数据竞争,因为每个线程都有自己的shared_ptr实例(尽管它们管理同一个对象A)。 您引用的文档描述了多个线程在同一shared_ptr实例上运行的场景。 在这种情况下,只能调用const成员函数(见下文),或者需要同步。
  • shared_ptr引用计数在aPtr超出main
  • 范围之前递增

是的,这是使用shared_ptr的正确方法。

  

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

您的代码不会引入数据竞争,既不能访问shared_ptr个实例,也不会访问托管对象A。 这意味着对多个线程执行的同一内存位置没有冲突的非原子读写操作。

但是,请注意,在checking()中,对IsOverMax()的调用与之后的实际工作分开 (Increase()可以在IsOverMax()之后但在“工作”之前由第二个线程调用。因此,当cur超过最大值时,您可以'做工作'。 这是否是一个问题取决于你的规范,但它被称为竞争条件,不一定是编程错误(不像导致未定义行为的数据竞争)。

  

对于非原子对象(例如使用std :: mutex来锁定对常规int的读写操作)会这样吗?

如果您使用cur保护它,则{p> int可以是常规std::mutex(非原子)。必须为写入 读取访问锁定互斥锁,以防止数据竞争。

关于在多个线程共享的对象上调用const成员函数的一句话 单独使用const并不能保证不会引入数据竞争 在这种情况下,保证适用于shared_ptr const成员函数,因为文档说明了这一点 我无法在 C ++标准中找到该保证是否适用于标准库

中的所有const成员函数

答案 1 :(得分:0)

该文档讨论的是shared_ptr的成员函数,而不是您班级的成员函数。 shared_ptr个对象的副本是不同的对象。

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

如果cur不是原子的,并且Increase()IsOverMax()std::mutex{"message":"Forbidden","name":"GoogleJsonResponseException","fileName":"SCHEDULER","lineNumber":204,"stack":"\tat SCHEDULER:204 (createAppointments)\n"}的访问都是通过锁定 Calendar.Events.insert(event, CAL, {sendNotifications: true, supportsAttachments:true}); 来保护的,那么该代码也是线程安全的。