【发布时间】:2011-05-17 14:38:23
【问题描述】:
谁能指出Copy-on-write (COW) 成语的线程安全实现? this site 上的示例代码看起来不错——它是线程安全的吗?
如果有人想知道我将使用它做什么:我有一个 Foo 类,它有一个 std::map<int,double> 成员。 Foo 对象在我的代码中被非常频繁地复制,但这些副本很少修改包含的 map。我发现与在 Foo 复制构造函数中复制整个地图内容相比,COW 给了我 22% 的性能提升,但是当使用多个线程时,我的 COW 实现会崩溃。
更新:
好的,这里是代码,简化为一个最小的例子,因为你要求它:
首先,引用计数图:
class RcMap {
public:
typedef std::map<int,double> Container;
typedef Container::const_iterator const_iterator;
typedef Container::iterator iterator;
RcMap() : count_(1) {}
RcMap(const RcMap& other) : count_(1) {
m_ = other.Get();
}
unsigned Count() const { return count_; }
unsigned IncCount() { return ++count_; }
unsigned DecCount() {
if(count_ > 0) --count_;
return count_;
}
void insert(int i, double d) {
m_.insert(std::make_pair(i,d));
}
iterator begin() { return m_.begin(); }
iterator end() { return m_.end(); }
const_iterator begin() const { return m_.begin(); }
const_iterator end() const { return m_.end(); }
protected:
const Container& Get() const { return m_; }
private:
void operator=(const RcMap&); // disallow
Container m_;
unsigned count_;
};
这里是包含这样一个映射RcMap 的类Foo,使用写时复制机制:
class Foo {
public:
Foo() : m_(NULL) {}
Foo(const Foo& other) : m_(other.m_) {
if (m_) m_->IncCount();
}
Foo& operator= (const Foo& other) {
RcMap* const old = m_;
m_ = other.m_;
if(m_ != 0)
m_->IncCount();
if (old != 0 && old->DecCount() == 0) {
delete old;
}
return *this;
}
virtual ~Foo() {
if(m_ != 0 && m_->DecCount() == 0){
delete m_;
m_ = 0;
}
}
const RcMap& GetMap() const {
if(m_ == 0)
return EmptyStaticRcMap();
return *m_;
}
RcMap& GetMap() {
if(m_ == 0)
m_ = new RcMap();
if (m_->Count() > 1) {
RcMap* d = new RcMap(*m_);
m_->DecCount();
m_ = d;
}
assert(m_->Count() == 1);
return *m_;
}
static const RcMap& EmptyStaticRcMap(){
static const RcMap empty;
return empty;
}
private:
RcMap* m_;
};
我还不能使用这个最小的示例重现崩溃,但在我的原始代码中,当我并行使用 Foo 对象的复制构造函数或赋值运算符时会发生这种情况。但也许有人能发现线程安全漏洞?
【问题讨论】:
-
有人可以解释如何使用比较和交换成语来实现它,正如这里所暗示的那样 (en.wikipedia.org/wiki/Copy-on-write)?
-
如果您的 COW 崩溃,如果您可以发布一个说明问题的最小代码示例,这可能会有所帮助。另外,你会遇到什么样的崩溃?
-
好的,我添加了一个最小的代码示例。崩溃发生在
delete m_的Foo析构函数中。 -
如果您可以使用 C++0x,我建议您将 move c'tor 与您的 COW 实现进行比较,看看哪个更快。
-
@Zach:这听起来不适合他的用例,因为他似乎真的想要一个对象的副本。
标签: c++ multithreading memory idioms