この記事の目的
6秒かかる直列処理を並行処理に改修し、3秒で終わるようにする
準備
まずは、適当にフォルダを作る
mkdir ~/Desktop/crystal
cd ~/Desktop/crystal/
つぎに、main.crファイルを作る
vim main.cr
まず直列実行するプログラムを書く
- 1秒待つコマンド
- 2秒待つコマンド
- 3秒待つコマンド
上記を順番に実行していくプログラムを main.cr
の中身を書く。
つまり、合計で6秒かかってしまう。
require "logger"
log = Logger.new(STDOUT)
log.info "started."
# 1秒かかるコマンド
log.info "sleep1 started."
sleep 1
log.info "sleep1 finished."
# 2秒かかるコマンド
log.info "sleep2 started."
sleep 2
log.info "sleep2 finished."
# 3秒かかるコマンド
log.info "sleep3 strated."
sleep 3
log.info "sleep3 finished."
log.info "all finished."
ともあれ、実行してみる
% crystal run main.cr
I, [2015-08-07 18:21:57 +0900 #9884] INFO -- : started.
I, [2015-08-07 18:21:57 +0900 #9884] INFO -- : sleep1 started.
I, [2015-08-07 18:21:58 +0900 #9884] INFO -- : sleep1 finished.
I, [2015-08-07 18:21:58 +0900 #9884] INFO -- : sleep2 started.
I, [2015-08-07 18:22:00 +0900 #9884] INFO -- : sleep2 finished.
I, [2015-08-07 18:22:00 +0900 #9884] INFO -- : sleep3 strated.
I, [2015-08-07 18:22:03 +0900 #9884] INFO -- : sleep3 finished.
I, [2015-08-07 18:22:03 +0900 #9884] INFO -- : all finished.
やはり、6秒かかった。
これを並行化したい。
spawnを使って並行化し、3秒で終わるようにする
Crystal言語には、Go言語のgo
のように処理を簡単に並行にできるspawn
がある。
ただし、spawn
だけでは、並行化した処理が終わる前に、メインの処理が終わってしまう。つまり、待ってくれない。各処理が終わるまで待つには、Channel
というAPIを使う。
なお、各ルーチンの実行結果を、呼び出し元に戻すときにもチャネルを使う。今回は、特に実行結果は必要ないので、チャネルに適当な値を入れることにする。ここでは、とりあえずbool値にする。
以上を踏まえて、さきほどの main.cr
を改修する:
require "logger"
log = Logger.new(STDOUT)
log.info "started."
channel = Channel(Bool).new
# 1秒かかるコマンド
spawn do
log.info "sleep1 started."
sleep 1
log.info "sleep1 finished."
channel.send true
end
# 2秒かかるコマンド
spawn do
log.info "sleep2 started."
sleep 2
log.info "sleep2 finished."
channel.send true
end
# 3秒かかるコマンド
spawn do
log.info "sleep3 strated."
sleep 3
log.info "sleep3 finished."
channel.send true
end
# 終わるまで待つ
3.times do
channel.receive
end
log.info "all finished."
実行してみよう
% crystal run main.cr
I, [2015-08-07 18:34:31 +0900 #10590] INFO -- : started.
I, [2015-08-07 18:34:31 +0900 #10590] INFO -- : sleep3 strated.
I, [2015-08-07 18:34:31 +0900 #10590] INFO -- : sleep2 started.
I, [2015-08-07 18:34:31 +0900 #10590] INFO -- : sleep1 started.
I, [2015-08-07 18:34:32 +0900 #10590] INFO -- : sleep1 finished.
I, [2015-08-07 18:34:33 +0900 #10590] INFO -- : sleep2 finished.
I, [2015-08-07 18:34:34 +0900 #10590] INFO -- : sleep3 finished.
I, [2015-08-07 18:34:34 +0900 #10590] INFO -- : all finished.
並行化ができて、3秒で終わるようになった\(^o^)/
まとめ
6秒かかる処理を、spawn
とChannel
を組み合わせて並行化し、3秒で終わるようになった。
関連
Goルーチンで並行化する方法: 6秒かかる処理を3秒にしよう - Qiita
更新
[2015-08-07]
Crystalの作者の @asterite さんから「終わるまで待つ」部分をもっとシンプルに書けると教えていただきました。ありがとうございます!