我有一个由多个任务处理的对象。我多次复制这个对象并存储在一个任务的向量中,以检索它自己的副本以在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将有大部分(如果不是全部)已经推送到矢量上的副本。
答案 0 :(得分:1)
首先,存在数据争用,因为从_poolIndex
(if
和taskIndex
中)读取的两个值未同步。交换它们并在条件下使用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(同时适用于ppl和tbb),您可能希望使用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 issue为size()
。因此,它需要与_objectPoolSize或基于零填充分配的构造函数的每项同步进行一些同步,如same blog中所述。