访问 shared_ptr 持有的 class 的原子成员
Accessing an atomic member of a class held by a shared_ptr
我正在尝试创建一个小的 class 来促进两个线程之间的通信。
这些线程很可能会在上面提到的 class 创建的上下文中存在,因为它们在线程池中排队。
到目前为止我已经尝试过的(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 说:
If multiple threads of execution access the same std::shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur unless all such access is performed through these functions, which are overloads of the corresponding atomic access functions (std::atomic_load, std::atomic_store, etc.)
- 所以
Increase
是一个非 const 函数,aPtr 的副本是否是 the same std::shared_ptr
对于这个上下文?
- 这段代码是线程安全的吗?
- 对于非原子对象(比如使用 std::mutex 来锁定对常规 int 的读写)是否可行?
- 无论如何为什么?
该文档讨论的是 shared_ptr
的成员函数,而不是 class 的成员函数。 shared_ptr
个对象的副本是不同的对象。
我相信代码是线程安全的,因为在不同线程上写入和读取的唯一变化变量是 cur
,并且该变量是原子的。
如果 cur
不是原子的并且在 Increase()
和 IsOverMax()
中对它的访问都通过锁定 std::mutex
来保护,那么该代码也将是线程安全的。
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
在 std::thread
创建时,aPtr
按值传递。因此,保证:
- 您不会引入数据竞争,因为每个线程都有自己的
shared_ptr
实例(尽管它们管理同一个对象 A
)。
您所指的文档描述了多个线程在同一个 shared_ptr
实例上运行的场景。
在那种情况下,只能调用const成员函数(见下文),或者需要同步。
shared_ptr
引用计数在 aPtr
超出 main
范围之前递增
所以是的,这是 shared_ptr
的正确使用方法。
Is this code thread-safe?
您的代码没有引入数据竞争,既没有访问 shared_ptr
个实例,也没有访问托管对象 A
。
这意味着没有冲突的、非原子的、由多个线程执行的对同一内存位置的读写操作。
但是,请记住,在 checking()
中,对 IsOverMax()
的调用与随后的实际工作是分开的
(Increase()
可以在 IsOverMax()
之后但在 'do work' 之前被第二个线程调用。因此,您可以在 cur
超过其最大值时“做工作”。
这是否是一个问题取决于您的规范,但它被称为竞争条件,不一定是编程错误(与导致未定义行为的数据竞争不同)。
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
cur
可以是常规 int
(非原子),如果你用 std::mutex
保护它。必须为写 和 读访问锁定互斥锁,以防止数据竞争。
关于在多个线程共享的对象上调用 const
成员函数的评论。
单独使用 const
并不能保证不会引入数据竞争。
在这种情况下,保证适用于 shared_ptr
const 成员函数,因为文档是这样说的。
我无法在 C++ 标准 中找到该保证是否适用于 标准库
中的所有 const 成员函数
我正在尝试创建一个小的 class 来促进两个线程之间的通信。
这些线程很可能会在上面提到的 class 创建的上下文中存在,因为它们在线程池中排队。
到目前为止我已经尝试过的(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 说:
If multiple threads of execution access the same std::shared_ptr object without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur unless all such access is performed through these functions, which are overloads of the corresponding atomic access functions (std::atomic_load, std::atomic_store, etc.)
- 所以
Increase
是一个非 const 函数,aPtr 的副本是否是the same std::shared_ptr
对于这个上下文? - 这段代码是线程安全的吗?
- 对于非原子对象(比如使用 std::mutex 来锁定对常规 int 的读写)是否可行?
- 无论如何为什么?
该文档讨论的是 shared_ptr
的成员函数,而不是 class 的成员函数。 shared_ptr
个对象的副本是不同的对象。
我相信代码是线程安全的,因为在不同线程上写入和读取的唯一变化变量是 cur
,并且该变量是原子的。
如果 cur
不是原子的并且在 Increase()
和 IsOverMax()
中对它的访问都通过锁定 std::mutex
来保护,那么该代码也将是线程安全的。
So Increase is a non const function, are the copies of aPtr are the same std::shared_ptr for this context or not ?
在 std::thread
创建时,aPtr
按值传递。因此,保证:
- 您不会引入数据竞争,因为每个线程都有自己的
shared_ptr
实例(尽管它们管理同一个对象A
)。
您所指的文档描述了多个线程在同一个shared_ptr
实例上运行的场景。 在那种情况下,只能调用const成员函数(见下文),或者需要同步。 shared_ptr
引用计数在aPtr
超出main
范围之前递增
所以是的,这是 shared_ptr
的正确使用方法。
Is this code thread-safe?
您的代码没有引入数据竞争,既没有访问 shared_ptr
个实例,也没有访问托管对象 A
。
这意味着没有冲突的、非原子的、由多个线程执行的对同一内存位置的读写操作。
但是,请记住,在 checking()
中,对 IsOverMax()
的调用与随后的实际工作是分开的
(Increase()
可以在 IsOverMax()
之后但在 'do work' 之前被第二个线程调用。因此,您可以在 cur
超过其最大值时“做工作”。
这是否是一个问题取决于您的规范,但它被称为竞争条件,不一定是编程错误(与导致未定义行为的数据竞争不同)。
Would this be OK for a non atomic object (say using an std::mutex to lock around reads and writes to a regular int)?
cur
可以是常规 int
(非原子),如果你用 std::mutex
保护它。必须为写 和 读访问锁定互斥锁,以防止数据竞争。
关于在多个线程共享的对象上调用 const
成员函数的评论。
单独使用 const
并不能保证不会引入数据竞争。
在这种情况下,保证适用于 shared_ptr
const 成员函数,因为文档是这样说的。
我无法在 C++ 标准 中找到该保证是否适用于 标准库