#はじめに
なんとなくやろうと思ってやっていなかった並列処理だが先日友人からsleep sortってしってる?と聞いて調べてみたところ並列処理をつかったソートっぽかたったので並列処理の練習を兼ねて実装しようと思った.
#sleep sort
実装内容は,
sleep( $SortData );
echo $SortData;
だけである.
この二行を並列処理でたくさん動かせば昇順に出力されるという算段である.
#まずは並列処理の動作確認
##その前に注意点
Linuxを使用している場合コンパイル時に
g++ -std=c++11 FileName.cpp -pthread
のように-std=c++11でc++11を使う,-pthreadで並列処理のライブラリにリンクをするといったオプションを付けなければならない.
Macの場合
clang -std=c++11 FileName.cpp
今回使用した並列処理のライブラリはである.
理由は個人的に資料が多いなと感じたことと,他と比べてわかりやすく感じたためである.
thread.cpp
#include <iostream>
#include <thread>
void outKokoax( void ){
std::cout << "kokoax" << std::endl;
}
int main( void ){
//コンストラクタを実行時にスレッドの生成と実行をしている.
std::thread MyName( outKokoax );
//タスクの終了ではなくスレッドそのものの終了を待つ.
//join()を実行しないと実行時にエラーが出る.
MyName.join();
return 0;
}
出力
kokoax
これだとよくわからないかもしれないがとりあえず関数をいつもと違うやり方で実行できたのでよしとした.
#sleep sortの実装
#include <iostream>
#include <iomanip>
#include <vector>
#include <thread>
#include <unistd.h>
#include <time.h>
void sleepSort( int sleepNumber ){
usleep( sleepNumber * 50000 ); //引数ミリ秒sleepする(unistd.h). *50000を消すと出力が重なって並列処理感がでる.
std::cout << std::setw(3) << std::left << sleepNumber;
}
int main( void ){
std::vector<std::thread> threads;
srand( (unsigned int)time(NULL) ); //現在時刻をrand()関数のシード値にする.
std::cout << "Before of sort"<< std::endl;
for( int i = 0; i < 10; i++ ){
int random = rand() % 30;
std::cout << std::setw(3) << std::left << random;
threads.push_back( std::thread( sleepSort, random ) ); //関数ポインタの次の引数はスレッドの引数に渡す値
/* ラムダ式でも書けるよ!
threads.push_back( thread( [random]() {
usleep( random * 50000 );
cout << random << endl;
}
)
);
*/
}
std::cout << std::endl << std::endl;
std::cout << "After of sort" << std::endl;
//threads.size()で配列のサイズを取得している.
for( int i = 0; i < threads.size(); i++ ){
//タスクの終了ではなくスレッドそのものの終了を待つ.
threads[i].join();
}
return 0;
}
#出力
Before of sort
2 16 26 2 5 27 9 27 3 24
After of sort
2 2 3 5 9 16 24 26 27 27
#追記 : std::asyncを使って指定したタイミングでスレッドを実行する.
上のthreadクラスでコンストラクタが実行された時はスレッドは実行されていなくて,join()メソッドを実行したときにスレッドが実行されるものだと勘違いしていた.
そこで,実際にそういったことをやれるとのことだったので追加で実装してみた.
C++入門 std::asyncさんを参考にコードを組んでみた.
#include <iostream>
#include <future>
#include <unistd.h>
void threadCheck( void ){
std::cout << "Start Running!" << std::endl;
}
int main( void ){
//スコープを分けている.
{
//launch::asyncを指定するとコンストラクタ実行時点でスレッドも実行される.
std::future<void> hereStart = std::async( std::launch::async, threadCheck );
usleep( 1000000 ); //1000000us -> 1s
std::cout << "End of Sleep!" << std::endl;
hereStart.get();
}
std::cout << std::endl;
{
//launch::deferredを指定すると.get()メッソドを実行した時にスレッドも実行される.
std::future<void> getStart = std::async( std::launch::deferred, threadCheck );
usleep( 1000000 ); //1000000us -> 1s
std::cout << "End of Sleep!" << std::endl;
getStart.get();
}
return 0;
}
実行結果
Start Running!
End of Sleep!
End of Sleep!
Start Running!
以上のように上のlaunch::asyncを指定して作った方はusleepよりも速く実行されているのに対し,deferredではsleepの後に実行されている.
この技術は遅延実行と呼ばれている.
#おわりに
並列処理と言われて思いつくのがwindwsAPIだが,なんだかhandlerがどうとかでかなり面倒くさいイメージ
があり,並列処理を敬遠していた,しかし<thread>ではそんなことはなく,関数のポインタを渡してしまえばあとはすぐ動かせる状態になり
かなり簡単だった.
また,思いがけずラムダ式やvectorを勉強できたので一石三鳥だった.
#参考文献
遥佐保の技術メモ C++/CXの旅(9):C++11の標準タスク処理のまとめ(std::future/promise/thread/async)