42

我打算声明一个原子变量向量,用作多线程程序中的计数器。这是我尝试过的:

#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++:\

4

4 回答 4

30

正如评论中提到的这个密切相关的问题std::atomic<T>中所述,它不是可复制的,也不是可复制的。

不具有这些属性的对象类型不能用作std::vector.

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

#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;
}

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

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

于 2012-11-02T11:47:39.990 回答
13

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

但是,由于缺少std::atomic<>对象的复制或移动构造函数,您对 的使用vector将受到限制,因为您在push_back(). 基本上你不能做任何会调用任何一个构造函数的事情。

这意味着您的向量的大小必须在构造时固定,并且您应该使用operator[]or来操作元素.at()。对于您的示例代码,以下工作1

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(). 限制是有道理的:通常erase()使用的连续存储块的中间std::deque需要移动元素,因此需要存在复制/移动构造函数或赋值运算符。

如果您不能忍受这些限制,您可以按照其他答案中的建议创建一个包装类,但要注意底层实现:一旦使用对象就移动它几乎没有意义std::atomic<>:它会破坏同时访问的任何线程对象。复制/移动构造函数的唯一合理使用通常是在将这些对象的集合发布到其他线程之前进行初始设置。


1除非,也许,您使用 Intel 的icc编译器,它在编译此代码时因内部错误而失败。

于 2017-10-13T18:46:06.130 回答
7

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

一种解决方法可能是用于vector<T>::emplace_back()在向量中就地构造原子。唉,我现在没有 C++11 编译器,否则我会去测试它。

于 2012-11-02T10:55:41.150 回答
0

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

我有一个用例,我想要 STL 映射的便利(特别是我使用映射映射来实现稀疏的二维原子矩阵,所以我可以做类似的事情int val = my_map[10][5])。在我的情况下,程序中只有这个映射的一个实例,所以它不会被复制,甚至更好的是我可以使用大括号初始化来初始化整个事物。所以非常不幸的是,虽然我的代码永远不会尝试复制单个元素或地图本身,但我被阻止使用 STL 容器。

我最终采用的解决方法是将 std::atomic 存储在 std::shared_ptr 中。这有优点,但也可能有缺点:

优点:

  • 可以在任何 STL 容器中存储 std::atomic
  • 不要求/限制仅使用某些 STL 容器方法。

赞成或反对(这方面的可取性取决于程序的用例): - 给定元素只有一个共享原子。所以复制 shared_ptr 或 STL 容器仍然会为元素生成一个共享原子。换句话说,如果你复制 STL 容器并修改其中一个原子元素,另一个容器对应的原子元素也会反映新的值。

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

以下是使用此方法初始化 std::vector 的简要语法:

#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]);
于 2018-02-23T13:04:05.323 回答