C++ 引用计数线程安全吗
例如,考虑C++ 引用计数线程安全吗,c++,multithreading,C++,Multithreading,例如,考虑 class ProcessList { private std::vector<std::shared_ptr<SomeObject>> list; Mutex mutex; public: void Add(std::shared_ptr<SomeObject> o) { Locker locker(&mutex); // Start of critical section. Locker release
class ProcessList {
private
std::vector<std::shared_ptr<SomeObject>> list;
Mutex mutex;
public:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
list.push_back(std::make_shared<SomeObject>(o).
}
void Remove(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex); // Start of critical section. Locker release so will the mutex - In house stuff
// Code to remove said object but indirectly modifying the reference count in copy below
}
void Process() {
std::vector<std::shared_ptr<SomeObject>> copy;
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
for (auto it = copy.begin(), it != copy.end(); ++it) {
it->Procss(); // This may take time/add/remove to the list
}
}
};
类进程列表{
私有的
std::向量表;
互斥互斥;
公众:
void Add(std::shared_ptr o){
锁柜(&mutex);//关键部分的开始。锁柜释放,互斥锁内部的东西也会释放
列表。向后推(标准::使共享(o)。
}
void Remove(std::shared_ptr o){
锁柜(&mutex);//关键部分的开始。锁柜释放,互斥锁内部的东西也会释放
//删除所述对象但间接修改以下副本中的引用计数的代码
}
无效过程(){
std::向量拷贝;
{
储物柜(和静音);
复制=标准::向量(
list.begin(),list.end()
)
}
对于(auto it=copy.begin(),it!=copy.end();+it){
it->Procss();//这可能需要时间/添加/删除到列表中
}
}
};
一个线程运行进程
。多个线程运行添加/删除
是否引用计数是安全的和总是正确的?还是应该在它周围放置互斥体?
< p>除非CPU架构具有原子增量/递减,并且使用它作为引用计数,那么,不,它不安全;C++不保证对其任何标准类型的X++/X操作的线程安全性。 如果编译器支持(C++11),请使用atomic
,否则需要使用锁
其他参考资料:
- 使用互斥锁进行引用计数将是一种开销
在内部,互斥体使用原子操作,基本上互斥体执行内部线程安全引用计数。因此,您可以直接使用原子进行引用计数,而不是使用互斥体,并且基本上做双倍的工作。是的,标准是(至少从N3997起,§20.8.2.2)这是为了要求引用计数是线程安全的
对于您的简单案例,如添加:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex);
list.push_back(std::make_shared<SomeObject>(o).
}
这将以原子操作的形式执行整个复制--在复制过程中,任何其他操作都无法修改列表。这将确保您的副本为您提供列表的快照,与启动复制时的快照完全相同。如果您消除互斥,引用计数仍将工作,但您的副本可能会反映在复制过程中所做的更改g制造
换句话说,shared_ptr
的线程安全性只确保每个单独的增量或减量是原子的——它不能确保整个列表的操作是原子的,就像互斥锁在这种情况下所做的那样
由于您的列表
实际上是一个向量
,因此您应该能够将复制代码稍微简化为copy=list
还请注意,您的Locker
似乎是std::lock\u guard
提供的一个子集。您似乎可以使用:
std::lock_guard<std::mutex> locker(&mutes);
std::锁护柜(&mutes);
…在它的位置非常容易。引用计数可以线程安全地实现。考虑到C++11也有线程,我假设refcounting也是以线程安全的方式实现的。顺便说一句:甚至Boost的原始
shared_ptr
也已经是线程安全的。每个引用计数都有互斥锁吗?不,它们使用原子操作。您可以对每个计数器使用一个互斥锁来实现它们,但这将是低效的。您也可以使用一个全局互斥锁来实现它们,这将更简单,但效率可能仍然低于使用原子的效率。@UlrichEckhardt的标准指令是这样的(那shared_ptr
是线程安全的)?如果是,你能提供一个参考吗?谢谢。第一个链接回答了这个问题。问题是std::shared_ptr
(这是OP提供的代码中使用的)是否是线程安全的。这里没有“x++/x--”操作(除非它在共享_ptr实现中)。但是vector线程也是安全的吗?我认为两者之间不是这样的。无论如何,我们的locker在日志记录方面做了一些额外的工作。@EdHeal:没有什么可以禁止vector实现提供线程安全,但也不是必需的。如果多个线程修改vector,那么是的,您需要向我们报告e一个互斥锁(或任何东西)来序列化访问。它是共享指针的向量。-所以这些项是安全的,但向量不是-是吗correct@EdHeal:是的,这就是我的想法。
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
std::lock_guard<std::mutex> locker(&mutes);