concurrent_vector vs vector with mutex,thread问题与push_back有关

时间:2014-11-15 16:41:01

标签: c++ multithreading vector parallel-processing concurrent-vector

我有一个由多个任务处理的对象。我多次复制这个对象并存储在一个任务的向量中,以检索它自己的副本以在parallel_for循环中工作。下面是带有标准向量的代码。

我的想法是从一个大小为0的向量开始,并根据并行启动的任务数量增长它,并需要自己的副本。我使用原子" _poolIndex"跟踪每次运行的全局索引。

    Object& GetObject()
    {
        if (_poolIndex >= _objectPool.size())
        {
            lock_guard<mutex> lock(_mutex);
            Object copy(_original);
            _objectPool.push_back(move(copy));
        }

        int taskIndex = _poolIndex.fetch_add(1);

        return _objectPool[taskIndex];
    }

我在vector类的下面的代码中得到索引超出范围,即使position&lt;调试器中断时的大小:

reference operator[](size_type _Pos)
{   // subscript mutable sequence
        #if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
    {   // report error
    _DEBUG_ERROR("vector subscript out of range");
    _SCL_SECURE_OUT_OF_RANGE;
    }

很明显,检索size()&lt; = _Pos的部分已经评估了不同的东西......我感到很困惑,因为我有一个锁定向量的方法。

然后我尝试了concurrent_vector,push_back给了我编译问题,这里是Visual Studio 2013错误:

  

错误35错误C2059:语法错误:&#39;&amp;&#39; c:\ program files   (x86)\ microsoft visual studio   12.0 \ vc \ include \ concurrent_vector.h 1492 1 UnitTests

     

错误36错误C2143:语法错误:缺少&#39 ;;&#39;之前&#39;)&#39; C:\程序   files(x86)\ microsoft visual studio   12.0 \ vc \ include \ concurrent_vector.h 1492 1 UnitTests

在concurrent_vector类中,当我从向量切换_objectPool为concurrent_vector时,这是给出问题的代码:

    void _Init(const void *_Src)
    {
        for(; _I < _N; ++_I)
            new( &_My_array[_I] ) _Ty(*static_cast<const _Ty*>(_Src));
    }

如果有人能就上述两个问题提供指导,那就太棒了。

我也试图尽量减少关键部分以提高效率。这个想法是在启动算法并运行多次后,_objectPool将有大部分(如果不是全部)已经推送到矢量上的副本。

1 个答案:

答案 0 :(得分:1)

问题

首先,存在数据争用,因为从_poolIndexiftaskIndex中)读取的两个值未同步。交换它们并在条件下使用taskIndex而不是再次读取共享状态。

Object& GetObject()
{
    int taskIndex = _poolIndex.fetch_add(1);
    if (taskIndex >= _objectPool.size())    // issue #2: size() is not thread-safe
    {
        lock_guard<mutex> lock(_mutex);
        //This: Object copy(_original);
        //      _objectPool.push_back(move(copy));
        // can be simplified to:
        _objectPool.push_back(_original); // issue #3: it can push at different index
    }

    return _objectPool[taskIndex];
}

在某些情况下,std::vector可能无法显示第二个问题。但它肯定会破坏concurrent_vector(see why)的使用。

第三个问题是taskIndex与锁定顺序不同步,因此它可以构造一个对象但返回尚未构造或分配(超出范围)。

正确的方式

如果我正确理解您的意图,您希望重用第一遍中创建的对象,并在需要时创建更多对象。我将尝试解决以下代码中的问题:

Object& GetObject()
{
    int taskIndex = _poolIndex.fetch_add(1);                // get current index in the pool
    if (taskIndex >= _objectPoolSize.load(memory_order_acquire)) // atomic<size_t>
    {
        lock_guard<mutex> lock(_mutex);
        size_t sz = _objectPoolSize.load(memory_order_relaxed);
        if (taskIndex >= sz) {                              // double-check under the lock
            sz *= 2;                  // or any other factor, get a bunch of new objects at once
            _objectPool.resize(sz, _original);              // construct new copies of _original
            _objectPoolSize.store(sz, memory_order_release);// protect from reorder with resize
        }
    }
    return _objectPool[taskIndex];
}

concurrent_vector

对于concurrent_vector(同时适用于),您可能希望使用grow_to_at_least来消除锁定..但是:

Object& GetObject()
{
    int taskIndex = _poolIndex.fetch_add(1); // get current index in the pool
    // construct new copies of _original if free objects is about to ran out
    _objectPool.grow_to_at_least(taskIndex+10/*or other*/, _original );
    return _objectPool[taskIndex];  // ISSUE: it might not be constructed yet in other thread
}

same issuesize()。因此,它需要与_objectPoolSize或基于零填充分配的构造函数的每项同步进行一些同步,如same blog中所述。