gpt4 book ai didi

c++ - 具有使用计数共享指针的多线程竞争条件

转载 作者:搜寻专家 更新时间:2023-10-31 01:43:31 26 4
gpt4 key购买 nike

我有一个旧的 C++98 代码库,它结合了使用计数和 pimpl 设计,多年来一直运行良好,直到 4 路和 8 路处理器变得普遍。在 pimpl 代码中你看到了这个(它实际上是真实代码中的一个模板):

class Foo {
FooImpl* impl;
public:
Foo() : impl(0) {}
Foo& operator=(const Foo& foo) {
if (foo.impl) foo.impl->addRef();
if (impl && impl->removeRef()) delete impl;
impl = foo.impl;
return *this;
}
// etc
}

问题是在多处理器环境中,任务切换可能发生在 if (foo.impl)foo.impl->addRef() 之间。解决这个问题的第一个想法可能是:

    Foo& operator=(const Foo& foo) {
FooImpl* fooimpl = foo.impl;
if (fooimpl) fooimpl->addRef();
if (impl && impl->removeRef()) delete impl;
impl = fooimpl;
return *this;
}

但这并不能解决问题。当您调用 addRef() 时,foo.impl 仍然会变得无效。有没有不涉及某种信号量的解决方法?如果对象上的每个赋值运算符都包含在信号量中,性能将会受到负面影响。

    Foo& operator=(const Foo& foo) {
global_lock.acquire();
if (foo.impl) foo.impl->addRef();
if (impl && impl->removeRef()) delete impl;
impl = foo.impl;
global_lock.release();
return *this;
}

我怀疑答案是否定的。

最佳答案

对于引用计数方案,需要以可以保证添加引用始终安全的方式设计和使用对象。作为一个关于为什么这应该是它的工作方式的思想实验,考虑两个线程引用同一个对象。在这种情况下,绝不应该出现其中一个线程遇到对象转换为 0 的引用计数的情况,因为它自己的引用应该可以防止这种情况发生。

在您所描述的问题中,解决方案应该是将引用传递给不同任务的任务可以继续并抢先添加引用。下面是一个说明这一点的方案:

class FooImpl {
friend class Foo;
unsigned count;
FooImpl() : count(1) {}
void addRef() { ++count; }
bool removeRef() { return !--count; }
};

class Foo {
FooImpl* impl;
void addRef() { if (impl) impl->addRef(); }
void removeRef() { if (impl && impl->removeRef()) delete impl; }
void swap(Foo &foo) {
FooImpl *tmp = impl; impl = foo.impl; foo.impl = tmp;
}
public:
~Foo() { removeRef(); }
Foo() : impl(0) {}
Foo(const Foo &foo) : impl(foo.impl) { addRef(); }
Foo& operator=(Foo foo) { swap(foo); return *this; }
// etc
};

请注意,虽然同一个 FooImpl 实例可能会被多个线程引用,但该方案要求每个线程维护自己的 Foo

关于c++ - 具有使用计数共享指针的多线程竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25067750/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com