[英]Is reference counting thread safe
例如考虑
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
}
}
};
一个线程运行Process
。 多个线程运行添加/删除。
引用计数是否安全且始终正确 - 或者是否应在其周围放置互斥锁?
是的,标准(至第20.2.2.2节,至少从N3997开始)是为了要求引用计数是线程安全的。
对于像Add
这样的简单案例:
void Add(std::shared_ptr<SomeObject> o) {
Locker locker(&mutex);
list.push_back(std::make_shared<SomeObject>(o).
}
...标准中的保证足够强大,您不需要互斥锁,因此您可以:
void Add(std::shared_ptr<SomeObject> o) {
list.push_back(std::make_shared<SomeObject>(o).
}
对于某些操作,虽然线程安全引用计数必然会消除您的互斥锁,但一点也不清楚。 例如,在您的Process
内部,您有:
{
Locker locker(&mutes);
copy = std::vector<std::shared_ptr<SomeObject>>(
list.begin(), list.end()
)
}
这将整个副本作为原子操作执行 - 在复制期间没有其他任何东西可以修改列表。 这样可以确保您的副本能够像启动副本时一样精确地为您提供列表的快照。 如果删除互斥锁,则引用计数仍然有效, 但您的副本可能反映了复制过程中所做的更改。
换句话说, shared_ptr
的线程安全性只能确保每个单独的增量或减量都是原子的 - 它不能保证整个列表的操作是原子的,就像在这种情况下互斥锁一样。
由于您的list
实际上是一个vector
,您应该能够将复制代码简化为copy = list
。
另请注意,您的Locker
似乎是std::lock_guard
提供的子集。 看来你可以使用:
std::lock_guard<std::mutex> locker(&mutes);
......相当容易。
使用互斥量进行引用计数将是一个开销。
在内部,互斥锁使用原子操作,基本上互斥锁执行内部线程安全引用计数。 因此,您可以直接使用原子进行引用计数,而不是使用互斥锁,而且基本上可以完成双重工作。
除非您的CPU架构具有原子递增/递减并且您将其用于引用计数,否则,它不安全; C ++不保证x ++ / x--在其任何标准类型上的操作的线程安全性。
如果您的编译器支持atomic<int>
,请使用atomic<int>
(C ++ 11),否则您将需要锁定。
进一步参考:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.