Help us understand the problem. What is going on with this article?

C++で簡単非同期処理(std::thread,std::async)

More than 5 years have passed since last update.

C++11では<future>ヘッダが導入され、
簡単に非同期の処理が実装できるようになった。
以下では用途毎に実装方法をまとめる。
thread間での同期の話はせず、あくまで完全に非同期な処理についてまとめる。

戻り値が必要ない場合(Thread-per-message pattern)

とりあえず非同期に実行できればいい場合。
実行時間を短縮するために複数の独立な処理を並列に実行するなどの応用が考えられる。
std::threadを使用する。最も基本的な使い方は以下の通りである:

auto th1 = std::thread([]{ do_long_work(); });
do_another_things();
th1.join();

std::threadの引数に実行したい関数を渡す。
関数に引数を与えたい場合は、std::thread(func, arg)のように行う。
プログラムが終了するまでにはjoin()しないといけない。
std::threadの引数のlambda式はキャプチャする事もできる:

double a;
auto th1 = std::thread([&a]{ a = long_calc(); });
do_another_things();
th1.join();
std::cout << a << std::endl; // 1.0

std::vector<std::thread>のようにコンテナに入れる事もできる。

#include <vector>
#include <future>
#include <iostream>

int main(int argc, char const *argv[]) {
  std::vector<int> v(10);
  std::vector<std::thread> threads;
  for (int i = 0; i < 10; ++i) {
    threads.push_back(std::thread([i, &v] { v[i] = i * i; }));
  }
  for (std::thread &th : threads) {
    th.join();
  }
  for (int i : v) {
    std::cout << i << std::endl;
  }
  return 0;
}

参考文献
- std::thread - cppreference
- std::threadをあとから開始。それとムーブ対応したコンテナについて (vectorに入れる部分)

コンパイル時の注意

コンパイルする際は並列用のライブラリをリンクする事を忘れずに。
例えばLinuxではpthreadをリンクする。

$ g++ -std=c++11 source.cpp -pthread
$ clang++ -std=c++11 source.cpp -pthread

リンク忘れると実行時にエラーになる:

terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted

結果を取得したい場合

上述の方法でも戻り値を参照でキャプチャした変数に代入する事ができているが、
これでは不便な場合も多い。
そこで登場するのがstd::asyncである。

auto result = std::async(std::launch::async, [] { return long_calc(); });
do_another_things();
std::cout << result.get() << std::endl;

resultの型はstd::future<T>(Tlong_calc()の戻り値の型)である。
result.get()ではじめて作成したスレッドをjoin()するので、
do_another_things()はthreadの生成直後に(long_calc()の実行を待たずに)実行される。

非同期処理をどうやって実行するか(Policy)

第一引数のstd::launch::asyncはpolicyを指定しており、
別スレッドを開始してlong_calc()を計算する、という指定である。
Policyは次の4通りの指定ができる

  • std::launch::async : 別スレッドで実行
  • std::launch::deferred : 遅延評価
  • std::launch::async | std::launch::deferred : 上記のいずれか(実装依存)
  • 指定なし : 両方指定した場合と同様

std::launch::deferredを指定した場合は単に遅延評価になる。
つまり最初にresult.get()が呼ばれたタイミングでlong_calc()を評価する。
次回以降にresult.get()が呼ばれたら最初の時に計算した値を返す。

std::async(std::launch::async | std::launch::deferred, func, arg)のように両方指定する場合、
あるいは単にstd::async(func, arg)のように省略した場合は、
async/deferredのどちらになるか実装依存である。

vectorを初期化する場合は以下のようにする:

#include <future>
#include <iostream>
#include <vector>

int main(int argc, char const *argv[]) {
  std::vector<std::future<int> > v;
  for (int i = 0; i < 10; ++i) {
    v.push_back(std::async([i] { return i * i; }));
  }
  for (auto &val : v) {
    std::cout << val.get() << std::endl;
  }
}

参考文献
- std::async - cppreference
- std::future - cppreference
- C++/CXの旅(9):C++11の標準タスク処理のまとめ(std::future/promise/thread/async)
- GNU/LinuxのC++11でプログラミングの常識がひっくりかえった(非同期の例外について詳しい)

Thread Pool (Worker Thread) pattern

C++でスレッドプール(ワーカースレッド)パターンを実装する方法は方々で議論されている。

参考文献

次回に詳しくまとめる。

ricos
FEMによる構造解析、機械学習の専門家集団。計算資源のクラウド提供もしています。
https://www.ricos.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした