2012-11-02 29 views
24

我打算声明一个要在多线程程序中用作计数器的原子变量向量。这里是我的尝试:如何在C++中声明一个原子矢量

#include <atomic> 
#include <vector> 

int main(void) 
{ 
    std::vector<std::atomic<int>> v_a; 
    std::atomic<int> a_i(1); 
    v_a.push_back(a_i); 
    return 0; 
} 

这是gcc 4.6.3的烦人详细的错误信息:

In file included from /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34:0, 
      from /usr/include/c++/4.6/bits/allocator.h:48, 
      from /usr/include/c++/4.6/vector:62, 
      from test_atomic_vec.h:2, 
      from test_atomic_vec.cc:1: 
/usr/include/c++/4.6/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(__gnu_cxx::new_allocator<_Tp>::pointer, const _Tp&) [with _Tp = std::atomic<int>, __gnu_cxx::new_allocator<_Tp>::pointer = std::atomic<int>*]’: 
/usr/include/c++/4.6/bits/stl_vector.h:830:6: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/ext/new_allocator.h:108:9: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:538:7: error: declared here 
In file included from /usr/include/c++/4.6/vector:70:0, 
      from test_atomic_vec.h:2, 
      from test_atomic_vec.cc:1: 
/usr/include/c++/4.6/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’: 
/usr/include/c++/4.6/bits/stl_vector.h:834:4: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/bits/vector.tcc:319:4: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:538:7: error: declared here 
/usr/include/c++/4.6/bits/stl_vector.h:834:4: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/bits/vector.tcc:319:4: error: use of deleted function ‘std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:539:15: error: declared here 
In file included from /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34:0, 
      from /usr/include/c++/4.6/bits/allocator.h:48, 
      from /usr/include/c++/4.6/vector:62, 
      from test_atomic_vec.h:2, 
      from test_atomic_vec.cc:1: 
/usr/include/c++/4.6/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(__gnu_cxx::new_allocator<_Tp>::pointer, _Args&& ...) [with _Args = {std::atomic<int>}, _Tp = std::atomic<int>, __gnu_cxx::new_allocator<_Tp>::pointer = std::atomic<int>*]’: 
/usr/include/c++/4.6/bits/vector.tcc:306:4: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_vector.h:834:4: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/ext/new_allocator.h:114:4: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:538:7: error: declared here 
In file included from /usr/include/c++/4.6/vector:61:0, 
      from test_atomic_vec.h:2, 
      from test_atomic_vec.cc:1: 
/usr/include/c++/4.6/bits/stl_algobase.h: In static member function ‘static _BI2 std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b(_BI1, _BI1, _BI2) [with _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’: 
/usr/include/c++/4.6/bits/stl_algobase.h:581:18: instantiated from ‘_BI2 std::__copy_move_backward_a(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_algobase.h:590:34: instantiated from ‘_BI2 std::__copy_move_backward_a2(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_algobase.h:661:15: instantiated from ‘_BI2 std::move_backward(_BI1, _BI1, _BI2) [with _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/vector.tcc:313:4: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_vector.h:834:4: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/bits/stl_algobase.h:546:6: error: use of deleted function ‘std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:539:15: error: declared here 
In file included from /usr/include/c++/4.6/vector:63:0, 
      from test_atomic_vec.h:2, 
      from test_atomic_vec.cc:1: 
/usr/include/c++/4.6/bits/stl_construct.h: In function ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::atomic<int>, _Args = {std::atomic<int>}]’: 
/usr/include/c++/4.6/bits/stl_uninitialized.h:77:3: instantiated from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*, bool _TrivialValueTypes = false]’ 
/usr/include/c++/4.6/bits/stl_uninitialized.h:119:41: instantiated from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_uninitialized.h:259:63: instantiated from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*, _Tp = std::atomic<int>]’ 
/usr/include/c++/4.6/bits/stl_uninitialized.h:269:24: instantiated from ‘_ForwardIterator std::__uninitialized_move_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::atomic<int>*, _ForwardIterator = std::atomic<int>*, _Allocator = std::allocator<std::atomic<int> >]’ 
/usr/include/c++/4.6/bits/vector.tcc:343:8: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’ 
/usr/include/c++/4.6/bits/stl_vector.h:834:4: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’ 
test_atomic_vec.cc:10:20: instantiated from here 
/usr/include/c++/4.6/bits/stl_construct.h:76:7: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’ 
/usr/include/c++/4.6/atomic:538:7: error: declared here 

如何解决这个问题?

当我用push_back()注释掉该行时,错误消失。

编辑:我编辑的帖子...对于那些你们谁看到了第一篇文章中,错误是令人尴尬的,我用gcc代替g++:\

+0

你试图添加-lstdC++标志? –

+0

与原子无关,完全没有关系。 –

+0

@Mat:correct ...但是问题出现在用'g ++'实现的代码中,只是我最小的例子被愚蠢地用'gcc'编译。请参阅编辑。 – steffen

回答

14

如在评论中提到的closely related question中所述,std::atomic<T>不是可复制构建的,也不是可复制分配的。

没有这些属性的对象类型不能用作std::vector的元素。

但是,应该可以创建一个围绕std::atomic<T>元素的可复制构建和可复制分配的包装器。它必须使用std::atomic<T>load()store()成员函数提供的建设和分配(这是公认的答案说明上述问题的想法):

#include <atomic> 
#include <vector> 

template <typename T> 
struct atomwrapper 
{ 
    std::atomic<T> _a; 

    atomwrapper() 
    :_a() 
    {} 

    atomwrapper(const std::atomic<T> &a) 
    :_a(a.load()) 
    {} 

    atomwrapper(const atomwrapper &other) 
    :_a(other._a.load()) 
    {} 

    atomwrapper &operator=(const atomwrapper &other) 
    { 
    _a.store(other._a.load()); 
    } 
}; 

int main(void) 
{ 
    std::vector<atomwrapper<int>> v_a; 
    std::atomic<int> a_i(1); 
    v_a.push_back(a_i); 
    return 0; 
} 

编辑:正如博正确地指出Persson,由包装器执行的复制操作不是原子的。它使您能够复制原子对象,但副本本身不是原子的。这意味着任何对原子的并发访问都不能使用复制操作。这意味着对矢量本身的操作(例如添加或删除元素)不得同时执行。

例如:如果一个线程修改存储在其中一个原子中的值,而另一个线程向该向量添加新元素,则可能会发生向量重新分配,并且第一个线程修改的对象可能从一个位置该向量到另一个。在这种情况下,第一个线程执行的元素访问和第二个线程触发的复制操作之间将存在数据竞争。

+0

似乎在做这项工作......这个解决方案有什么缺点吗?如果不是......为什么不在std :: atomic中实现呢? – steffen

+3

一个问题是'_a.store(other._a。load());'对我来说看起来不太原子。这有用吗? –

+0

@steffen我意识到的唯一缺点是实现必须采取一切必要的预防措施,以确保所有副本和分配均以原子方式执行。这可能涉及内存隔离和锁定,因此会减慢向量中元素的插入以及在向量本身上执行的重新分配和复制操作。但这就是与原子力工作的意思。您可以将特殊的内存模型参数作为参数传递给'load'和'store'调用来改进事物,但选择正确的内存模型(除了安全的默认模型之外)是一门艺术。 – jogojapan

7

在我看来就像atomic<T>没有副本构造函数。据我所知,也不是移动构造函数。

一个解决办法可能是使用vector<T>::emplace_back()来构造向量中的原子。唉,我现在还没有C++ 11编译器,或者我会去测试它。

+0

这似乎是问题所在。把'v_a.push_back(std :: move(a_i));'不解决它。 – steffen

+0

@steffen你说得对,原子没有移动构造函数。我已经更新了我的答案,其解决方案与已接受的解决方案不同。 –

+1

我试过使用'emplace_back(1)'而不是'push_back(a_i)',但是GCC 7.2拒绝了,说必要的单元复制操作需要复制构造函数。我想这是因为插入新元素时可能的重新分配。无论如何,即使某些编译器接受它,它仍然是不正确的使用'std :: vector',至少在形式上,因为你不应该使用不可复制分配的元素类型。 – jogojapan

1

要首先回答您的标题问题:您可以轻松地声明 a std::vector<std::atomic<...>>,就像您在示例中所做的那样。

由于缺乏复制或移动构造为std::atomic<>对象,然而,当你与编译错误上push_back()发现了你的vector的使用将受到限制的。基本上你不能做任何会调用构造函数的东西。

这意味着您的向量大小必须在构建时固定,并且您应该使用operator[].at()来操作元素。为了您的示例代码,下面的工作:

std::vector<std::atomic<int>> v_a(1); 
std::atomic<int> a_i(1); 
v_a[0] = a_i; 

如果限制“在建设固定大小”太麻烦,你可以使用std::deque代替。这让无论你布设对象,而无需复制或移动构造函数动态增长的结构,例如:

std::deque<std::atomic<int>> d; 

d.emplace_back(1); 
d.emplace_back(2); 
d.pop_back(); 

仍然有一定的局限性。例如,您可以使用pop_back(),但不能使用更通用的erase()。这些限制是有意义的:在std::deque所使用的连续存储块的中间的erase()通常需要元素的移动,因此需要复制/移动构造函数或赋值操作符存在。

如果你不能与那些限制居住,你可以创建一个包装类在其他的答案建议,但要注意的底层实现的:它使毫无意义到移动std::atomic<>对象一旦正在使用:它会打破任何线程并发访问对象。复制/移动构造函数的唯一合理使用通常在这些对象的集合初始设置为其他线程之前进行初始设置。


除非,也许,您使用英特尔的编译器icc,这fails with an internal error在编译的代码。

+0

评论不适用于扩展讨论;这个对话已经[转移到聊天](http://chat.stackoverflow.com/rooms/157039/discussion-on-answer-by-beeonrope-how-to-declare-a-vector-of-atomic-in- C)。 – Andy

0

正如其他人已经正确指出,编译器的错误的原因是std :: atomic显式禁止复制构造函数。

我有一个用例,我希望STL地图的方便(特别是我使用地图的地图来实现原子的稀疏二维矩阵,所以我可以执行类似int val = my_map[10][5]的操作)。在我的情况下,程序中只有一个此映射的实例,因此它不会被复制,甚至更好,我可以使用加载初始化来初始化整个事件。所以非常不幸的是,虽然我的代码永远不会试图复制个别元素或映射本身,但我无法使用STL容器。

我最终使用的解决方法是将std :: atomic存储在std :: shared_ptr中。这有优点,但可能是一个骗子:

优点:

  • 可存储的std ::任何STL容器
  • 不要求/限制只使用STL容器的某些方法中的原子。

Pro或CON(此方面的合意性依赖于节目用例): - 只有单个共享原子对于给定的元件。因此,复制shared_ptr或STL容器仍然会为元素生成单个共享原子。换句话说,如果您复制STL容器并修改其中一个原子元素,则另一个容器的相应原子元素也将反映新值。

在我的情况下,由于我的使用情况,Pro/Con的特性没有实际意义。

这里有一个简单的语法来初始化一个std ::矢量用这种方法:

#include <atomic> 
#include <memory> 
#include <vector> 

std::vector<std::shared_ptr<std::atomic<int> > > vecAtomicInts 
{ 
    std::shared_ptr<std::atomic<int> >(new std::atomic<int>(1)), 
    std::shared_ptr<std::atomic<int> >(new std::atomic<int>(2)), 
}; 

// push_back, emplace, etc all supported 
vecAtomicInts.push_back(std::shared_ptr<std::atomic<int> >(new std::atomic<int>(3))); 

// operate atomically on element 
vecAtomicInts[1]->exchange(4); 

// access random element 
int i = *(vecAtomicInts[1]);