我正在尝试创建一个允许我促进两个线程之间通信的小类。
这些线程很可能比在线程池中排队时创建上述类的上下文更长。
到目前为止我尝试过的(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
?所以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()
之后但在“do work”之前由第二个线程调用)。因此,当cur
超过最大值时,你可以'做工作'。这是否是一个问题取决于您的规范,但它被称为竞争条件,不一定是编程错误(不像导致未定义行为的数据竞争)。
这对于非原子对象是否可行(比如使用std :: mutex来锁定对常规int的读写)?
如果用cur
保护它,int
可以是常规的std::mutex
(非原子)。必须锁定互斥锁以进行写入和读取访问,以防止数据竞争。
关于在多个线程共享的对象上调用const
成员函数的一句话。
单独使用const
并不能保证不会引入数据竞争。
在这种情况下,保证适用于shared_ptr
const成员函数,因为文档说明了这一点。
我在C ++标准中找不到该保证是否适用于标准库中的所有const成员函数
该文档讨论的是shared_ptr
的成员函数,而不是类的成员函数。 shared_ptr
对象的副本是不同的对象。
我相信代码是线程安全的,因为在不同的线程上编写和读取的唯一更改变量是cur
,并且该变量是原子的。
如果cur
不是原子的,并且在Increase()
和IsOverMax()
中都可以通过锁定std::mutex
来保护它,那么该代码也是线程安全的。