概要
C++のよく知られたライブラリとしてBoostがあります。このBoostのネットワークライブラリとしてAsioがあります。
Boost.AsioにはOSとの仲立ちをしてくれる機能としてboost::asio::io_serviceがあります。
ここでは、これを利用した非同期実行の方法を書いていきます。
io_serviceにできること
シングルスレッドでの非同期実行。Node.jsみたいな感じで単一スレッド上でキューに入っている処理を非同期に実行してくれます。
マルチスレッドの非同期実行。シングルスレッドでもそこそこに面倒な非同期処理をマルチスレッドでも可能にしてくれます。
とりあえず実行してみる
シングルスレッドでのソースと実行結果を載せます。
// clang++ -std=c++11 -lboost_system test1.cpp
#include <iostream>
#include <boost/asio/io_service.hpp>
namespace {
// とりあえずの処理1
void func1() {
std::cout<<"func1"<<std::endl;
}
// とりあえずの処理2
void func2() {
std::cout<<"func2"<<std::endl;
}
}
int main() {
boost::asio::io_service ios;
// io_serviceの中にあるキューに追加する
for(std::size_t i=0; i<2; ++i) {
ios.post(func1);
ios.post(func2);
}
// キューがなくなるまで実行を続ける
ios.run();
}
実行結果
func1
func2
func1
func2
これだけでは何も有り難みがありませんが、通信などいつ処理が起きるかわからないような用途ではちょうど良い仕組みだと思います。
マルチスレッドで実行
シングルスレッドでもマルチスレッドでもある程度の処理は同じです。(内部の処理は全然違うかもしれませんが)
シングルスレッドとの一番の違いはrun
を複数のスレッドで呼ぶことです。
io_service
ではrun
が呼ばれたスレッドで処理を実行していきます。
とりあえず書いてみる
// clang++ -std=c++11 -lboost_system -lpthread test.cpp
#include <thread>
#include <iostream>
#include <boost/asio/io_service.hpp>
namespace {
// とりあえずの処理1 改(1秒停止)
void func1() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout<<"func1"<<std::endl;
}
// とりあえずの処理2 改(3秒停止)
void func2() {
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout<<"func2"<<std::endl;
}
}
int main() {
boost::asio::io_service ios;
// io_serviceの中にあるキューに追加する
for(std::size_t i=0; i<2; ++i) {
ios.post(func1);
ios.post(func2);
}
// キューがなくなるまで実行を続ける
// スレッド
std::thread t([&ios]{ios.run();});
// メインスレッド
ios.run();
t.join();
}
実行結果
func1
func1
func2
func2
このように複数のスレッドで実行した場合、処理が終わったスレッドから順次処理を消化してくれます。
postとdispatch
io_serviceにはpost
とdispatch
メンバ関数があります。この関数の違いは処理が扱われ方にあります。
post
は普通にキューに処理を追加し順番が回ってきたら自動的に実行されます。
dispatch
はキューとは関係なく処理を一時停止し、渡された処理をその場で実行します。
余談
C++ではC++11からstd::thread
が標準になりましたが、複数のスレッドをまとめて立てるときはコンテナに入れる方法しかありません。また、コンテナに収まっていると言ってもすべて別々のスレッドクラスであるため一つ一つの項目に対してjoin()
を呼ばないといけないためめんどくさいです。
そこで、あえて標準のスレッドを使わず、Boostのスレッドを使う手があります。Boostのスレッドにはthread_group
というクラスがあり、このクラスは属するスレッドをまとめてjoin
できるjoin_all
という関数があります。
参考
テストプログラムの環境
OS : Linux Mint 18(x64)
コンパイラ : clang++ 3.8.0
Boost : 1.58.0