web-dev-qa-db-ja.com

独立したstd :: threadsのC ++ std :: vector

main()にメインの無限ループがあり、データの読み取りと処理に使用されるスレッドがあるリアルタイムソフトウェアを構築しています。

問題の1つは、実行中のスレッドのstd::vectorを保持して、それらにシグナルを送信し、実行を監視することです。だから私はこのコードをまとめました:

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>

namespace readerThread {

    void start(int id)
    {
        while (1)
        {
            std::cout << "Reader " << id << " running..." <<  std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }

}


int main() 
{

        int readers[] = { 1, 2, 3 };

        std::vector<std::thread> readerThreads;

        for (int &reader : readers)
        {
            std::thread th(readerThread::start, reader);
            readerThreads.Push_back(th);
        }

        while(true)
        {
            std::cout << "Waiting..." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(10000));
        }

        return 0;
}

コンパイルすらしないので、次のエラーが発生します。

In file included from /usr/local/include/c++/5.1.0/x86_64-unknown-linux-gnu/bits/c++allocator.h:33:0,
                 from /usr/local/include/c++/5.1.0/bits/allocator.h:46,
                 from /usr/local/include/c++/5.1.0/string:41,
                 from /usr/local/include/c++/5.1.0/bits/locale_classes.h:40,
                 from /usr/local/include/c++/5.1.0/bits/ios_base.h:41,
                 from /usr/local/include/c++/5.1.0/ios:42,
                 from /usr/local/include/c++/5.1.0/ostream:38,
                 from /usr/local/include/c++/5.1.0/iostream:39,
                 from main.cpp:1:
/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = {const std::thread&}; _Tp = std::thread]':
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4:   required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]'
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16:   required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30:   required from 'void std::vector<_Tp, _Alloc>::Push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]'
main.cpp:37:30:   required from here
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)'
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^
In file included from main.cpp:4:0:
/usr/local/include/c++/5.1.0/thread:126:5: note: declared here
     thread(const thread&) = delete;
     ^

スレッドは独立しているので、メインプログラムでもスレッドでもjoinを呼び出す必要はありません...

だから、ここに私の疑問があります:

コードがコンパイルされないのはなぜですか?

それはスレッドのベクトルを保存する正しい方法ですか?

助けてくれてありがとう...

PS: 元のコードはこちら

11
Mendes

あなたは次のようなものを使用する必要があります

readerThreads.Push_back(move(th));

これにより、thが右辺値になり、movectorが呼び出されます。 threadのコピーctorは、設計上 無効 でした(Anthony Williamsの C++ Concurrency In Action を参照)。

15
Ami Tavory
_/usr/local/include/c++/5.1.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::thread; _Args = {const std::thread&}; _Tp = std::thread]':
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:256:4:   required from 'static std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; std::_Require<std::allocator_traits<_Alloc>::__has_construct<_Tp, _Args ...> > = void]'
/usr/local/include/c++/5.1.0/bits/alloc_traits.h:402:16:   required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::thread; _Args = {const std::thread&}; _Alloc = std::allocator<std::thread>; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
/usr/local/include/c++/5.1.0/bits/stl_vector.h:917:30:   required from 'void std::vector<_Tp, _Alloc>::Push_back(const value_type&) [with _Tp = std::thread; _Alloc = std::allocator<std::thread>; std::vector<_Tp, _Alloc>::value_type = std::thread]'
main.cpp:37:30:   required from here
/usr/local/include/c++/5.1.0/ext/new_allocator.h:120:4: error: use of deleted function 'std::thread::thread(const std::thread&)'
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
_

これを少しはがしてみましょう。

_error: use of deleted function 'std::thread::thread(const std::thread&)'
_

あなたのコードは_std::thread_を導入しようとする何かをしています。

_required from 'void std::vector<_Tp, _Alloc>::Push_back(const value_type&)
_

_Push_back_が原因です。

_std::thread_はコピーできません-スレッドをコピーするのは意味ですか?

_std::thread t1([](){});
std::thread t2 = t1;
_

したがって、_std::thread_オブジェクトのインスタンスは、一意の所有者になることを目的としています。単純な混乱は別として、多くの苦痛が続くでしょう。

ただし、それらは移動可能です。

_std::thread t1([](){});
std::thread t2 = std::move(t1);
_

_t1_はもはや有効なスレッド記述子ではなく、それが記述していたスレッドは現在_t2_によって所有されています。

そのようなものをコンテナに入れるには、_std::move_または_std::emplace_/_std::emplace_back_のいずれかを使用できます。

_std::vector<std::thread> threads;
threads.Push_back(std::move(std::thread([](){})));
threads.emplace_back([](){});
_

あなたのコードがこの特定の問題に焦点を合わせている間、C++標準は、スレッドがまだ接続されていない間に呼び出されるスレッドデストラクタのエラーとしてそれを宣言していることを指摘しておきます。

_int main() {
    std::thread t1([](){ while (true) { std::this_thread::yield(); } };
}
_

Mainが終了すると、t1。〜thread()が呼び出され、スレッドがまだ接続されていて結合されていないことが検出されます。これにより例外が発生し、シャットダウンがクラッシュします。

スレッドをjoin()して実行が終了するのを待つか、detach()する必要があります。 join()を使用したい場合、およびdetach()を使用した場合、プログラムがスレッドの途中で終了し、データの書き込みなどを行う場合は、スレッドに停止を指示する方法が必要になります。 、重大なバグが発生する可能性があります。

_#include <thread>
#include <chrono>
#include <future>

int main () {
  std::promise<void> cnx_promise;
  std::shared_future<void> cnx_future;

  std::thread t1([cnx_future]() {
      while (cnx_future.valid()) {
        std::this_thread::yield();
      }
  });

  std::this_thread::sleep_for(std::chrono::seconds(1));

  cnx_promise.set_value();

  t1.join();
}
_

ここでは、promiseを使用して、実行を停止する時期をスレッドに通知しますが、条件変数、シグナルなど、またはfalseをテストする単純な_std::atomic<bool> ok_to_run { true };_を使用することもできます。

6
kfsone

動作する別のバリ​​アントは、vector.Push_back呼び出しでスレッドオブジェクトを作成することです。この場合、すでに右辺値であるため、std :: moveを呼び出す必要はありません(したがって、移動されます)。

for (int &reader : readers)
    readerThreads.Push_back(std::thread(readerThread::start, reader));
3
Seth

これは、すべてのスレッドがベクターのストレージに作成されることを保証します。オブジェクトバイトはコピーされません。

for (int &reader : readers)
    readerThreads.emplace_back( readerThread::start, reader );

一般的なケースでは、これは@Sethが提案したものと同じではありません。しかし、現在の場合、99%同じです。

1
kyb