web-dev-qa-db-ja.com

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()で行をコメントアウトすると、エラーは消えます。

edit:投稿を編集しました...最初の投稿を見た人は、恥ずかしいことに、_g++_の代わりにgccを使用したというエラーが発生しました:\

34
steffen

この 密接に関連する質問 で説明されているように、コメントで言及されているように、_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;
}
_

編集:Bo Perssonによって正しく指摘されているように、ラッパーによって実行されるコピー操作はアトミックではありません。アトミックオブジェクトをコピーできますが、コピー自体はアトミックではありません。これは、アトミックへの同時アクセスでは、コピー操作を利用してはならないことを意味します。これは、ベクター自体に対する操作(要素の追加や削除など)を同時に実行してはならないことを意味します。

例:たとえば、あるスレッドが1つのアトミックに格納されている値を変更し、別のスレッドが新しい要素をベクターに追加すると、ベクターの再割り当てが発生し、最初のスレッドが変更したオブジェクトがベクターのある場所から別の場所にコピーされる場合があります。 。その場合、最初のスレッドによって実行される要素アクセスと2番目のスレッドによってトリガーされるコピー操作の間にデータ競合が発生します。

19
jogojapan

最初に見出しの質問に答えるには、例で行ったように、簡単にdeclare a std::vector<std::atomic<...>>を使用できます。

ただし、std::atomic<>オブジェクトのコピーまたは移動コンストラクターがないため、Push_back()のコンパイルエラーで判明したように、vectorの使用は制限されます。基本的に、どちらのコンストラクタも呼び出すことはできません。

つまり、ベクトルのサイズは構築時に固定する必要があり、operator[]または.at()を使用して要素を操作する必要があります。あなたのサンプルコードでは、次の作品1

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

「構築時の固定サイズ」の制限が面倒な場合は、代わりにstd::dequeを使用できます。これにより、emplaceオブジェクトを使用して、コピーまたは移動コンストラクターを必要とせずに構造を動的に拡張できます。例:

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<>オブジェクトを移動する少し意味があるになります:オブジェクトに同時にアクセスしているすべてのスレッドを中断します。他のスレッドに公開される前のこれらのオブジェクトのコレクションの初期設定では、コピー/移動コンストラクターの唯一の正常な使用が一般的です。


1 おそらく、このコードをコンパイルするときに、インテルのiccコンパイラーを使用しない限り、 内部エラーで失敗 になります。

9
BeeOnRope

_atomic<T>_にはコピーコンストラクタがないように見えます。私の知る限り、移動コンストラクタもありません。

回避策の1つは、vector<T>::emplace_back()を使用して、アトミックなベクター内のアトミックを構築することです。悲しいかな、私には今C++ 11コンパイラがありません、または私はそれをテストします。

7
Kaz Dragon

他の人が適切に指摘したように、コンパイラのエラーの原因は、std :: atomicがコピーコンストラクタを明示的に禁止していることです。

STLマップの利便性が必要なユースケースがありました(具体的には、アトミックのスパース2次元マトリックスを実現するためにマップのマップを使用していたため、int val = my_map[10][5])。私の場合、プログラムにはこのマップのインスタンスが1つしかないため、コピーされず、ブレース初期化を使用して全体を初期化できます。そのため、私のコードでは個々の要素やマップ自体をコピーしようとはしませんでしたが、STLコンテナーを使用できなかったことは非常に残念でした。

私が最終的に行った回避策は、std :: atomicをstd :: shared_ptr内に格納することです。これには長所がありますが、おそらく短所があります:

長所:

  • Std :: atomicを任意のSTLコンテナー内に格納できます
  • 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]);
0
codesniffer