web-dev-qa-db-ja.com

std :: threadを一時停止および再開する正しい方法

std::threadコードでC++を使用して、常にいくつかのデータをポーリングし、それをバッファに追加しています。 C++ lambdaを使用して、次のようにスレッドを開始します。

StartMyThread() {

    thread_running = true;
    the_thread = std::thread { [this] {
        while(thread_running) {
          GetData();
        }
    }};
}

thread_runningは、クラスヘッダーで宣言されたatomic<bool>です。これが私のGetData関数です:

GetData() {
    //Some heavy logic which needs to be executed in a worker thread
}

次にStopMyThread関数もあり、thread_runningをfalseに設定して、lambda blockのwhileループから抜け出します。

StopMyThread() {
  thread_running = false;
  the_thread.join();
}

それはうまくいきます。スレッドはクラッシュすることなく開始および停止します。

このC++コードは、iOS、Android、OS X、およびWindowsで使用されます。私のアプリケーションUIにはボタンがあり、ボタンを押すとスレッドを開始および停止する必要があります。このボタンは、場合によっては頻繁に使用できます。スレッドの停止または開始中に、UIに一瞬の遅延が表示されます。

私の質問は:C++では、これはスレッドを頻繁に開始/停止する正しい方法ですか?私はこれでロジック毎回新しいスレッドを作成しています。そして、私が理解しているように、新しいスレッドを作成すると、OSは多くの新しいリソースを割り当て、時間を節約できます。そして、これは私がしている間違いだと思います。どうすればこれを回避できますか?

アプリケーションのライフサイクル全体で新しいスレッドを繰り返し割り当てずに、同じスレッドを使用して、必要に応じて再生/一時停止するにはどうすればよいですか?

これは、条件変数の使用の古典的な例です。ミューテックスで待機し、特定の条件が満たされたときにスレッドに通知します。この方法では、必要なときに新しいスレッドを割り当てる必要はありませんが、メモリを節約したい場合、これは常に良いことではありません。代わりの方法は、データが必要なときにコルーチンが別のコルーチンに譲ることです。コルーチンを自分で実装するか、boost.coroutineなどの既成のライブラリを使用する必要があります。

::std::condition_variable cv_;
::std::mutex m_;
bool data_is_ready_{};

StartMyThread()
{
  ::std::thread([this]
    {
      for (;;)
      {
        ::std::unique_lock<decltype(m_)> l(m_);
        cv.wait(l, [this]{ return data_is_ready_; });

        // do your stuff, m_ is locked
        data_is_ready_ = false;
      }
    }
  ).detach();
}

通知するには:

{
  ::std::unique_lock<decltype(m_)> l(m_);

  data_is_ready_ = true;
}

cv_.notify_one();

多くの場合、通知する前にロックを解放する方が、逆の場合よりも高速です。

5
user1095108