C ++中的多线程read-many,write-seldom array / vector迭代

时间:2013-03-13 13:59:28

标签: c++ performance stl locking

我需要以只读方式几乎不断迭代一系列结构,但是对于每1M +读取,其中一个线程可能附加一个项目。我认为在这里使用互斥锁会有点过分,我也会在某处读到r / w lock对读者有其自身的缺点。

我正在考虑在std :: vector上使用reserve(),但这个答案Iterate over STL container using indices safe way to avoid using locks?似乎使这个无效。

关于哪种方式的想法可能最快?最重要的是让读者能够以尽可能少的争用快速有效地进行迭代。写作操作不是时间敏感的。

更新:我的另一个用例是“列表”可能包含指针而不是结构。即,std :: vector。相同的要求适用。

更新2:假设的例子

全球可访问:

typedef std::vector<MyClass*> Vector;
Vector v;
v.reserve(50);

阅读器主题1-10 :(这些运行一直在运行)

.
.
int total = 0;
for (Vector::const_iterator it = v.begin(); it != v.end(); ++it)
{
   MyClass* ptr = *it;
   total += ptr->getTotal();
}
// do something with total
.
.

作家帖子11-15:

MyClass* ptr = new MyClass();
v.push_back(ptr);

这基本上就是这里发生的事情。线程1-15可以同时运行,尽管通常只有1-2个读取线程和1-2个写入器线程。

2 个答案:

答案 0 :(得分:2)

我认为可以在这里工作的是vector的自己实现,如下所示:

template <typename T> class Vector
{
// constructor will be needed of course
public:
    std::shared_ptr<const std::vector<T> > getVector()
        { return mVector; }
    void push_back(const T&);

private:
    std::shared_ptr<std::vector<T> > mVector;
};

然后,只要读者需要访问特定的Vector,他们就应该致电getVector()保留返回的shared_ptr,直到读完为止。

但是作者应该始终使用Vector的{​​{1}}来添加新值。然后,此push_back应检查是否为push_back,如果为真,请分配 mVector.size() == mVector.capacity()并将其分配给vector。类似的东西:

mVector

这里的想法受到RCU(读取 - 复制 - 更新)算法的启发。如果存储空间耗尽,只要至少有一个读取器访问旧存储,新存储不应使旧存储无效。但是,应该分配新存储,并且任何读者在分配之后都应该能够看到它。只要没有人使用旧存储,就应该取消分配旧存储(所有读者都已完成)。

由于大多数硬件架构提供了一些原子递增和递减的方法,我认为template <typename T> Vector<T>::push_back(const T& t) { if (mVector->size() == mVector->capacity()) { // make certain here that new_size > old_size std::vector<T> vec = new std::vector<T> (mVector->size() * SIZE_MULTIPLIER); std::copy(mVector->begin(), mVector->end(), vec->begin()); mVector.reset(vec); } // put 't' into 'mVector'. 'mVector' is guaranteed not to reallocate now. } (因此shared_ptr)将能够完全无锁地运行。

这种方法的一个缺点是,根据读者持有Vector的时间长短,您最终可能会得到shared_ptr的多个副本。

PS:希望我没有在代码中犯下太多令人尴尬的错误: - )

答案 1 :(得分:0)

  

...在std :: vector上使用reserve()......

如果保证 你已经声明如果项目上面的数字,那么你就无法保证。

尽管存在相关问题,但您可以设想使用std::vector来管理内存,但是需要额外的逻辑层才能解决接受答案中确定的问题。


实际答案是:最快的做法是尽量减少同步量。 的最小同步量取决于您未指定的代码和用法的详细信息。


例如,我使用固定大小的块的链表列出了一个解决方案。这意味着您的常见用例应该与数组遍历一样高效,但是您可以在不重新分配的情况下动态增长。

然而,实施结果对以下问题很敏感:

  • 是否需要删除项目
    • 无论何时阅读?
    • 仅从前方或其他地方?
  • 您是否希望读者忙 - 等待容器是空的
    • 这是否应该使用某种退避
  • 需要多大程度的一致性?