Crystal には Thread がなくてつらい

これは「Ruby脳にはCrystalつらい Advent Calendar 2015」の22日目の記事です。

qiita.com

タイトルのまんまですが、Crystal は Thread がなくてつらい。

Thread というクラスはありますが、ソースを見ると、

  # Don't use this class, it is used internally by the event scheduler.
  # Use spawn and channels instead.

と書いてあって気軽に使える雰囲気ではありません。

spawn や channel を使えと書かれてるので spawn を使ってみます。

spawn do
  10.times do |i|
    print 'a'
    sleep 1
  end
end

spawn do
  10.times do |i|
    print 'b'
    sleep 1
  end
end

gets

このプログラムを動かすと次のようになります。

% crystal hoge.cr 
bababababaababababab

a と b が交互に出力されて、ちゃんと並列動作しているようです。

なお、sleep がないと交互にはなりません。spawn は Fiber で実装されていて、sleep によってコンテキストを切り替えているようです。 つまりスレッドと違い、複数 CPU を同時に使うことはできません。

上の例では最後に gets でキー入力を待っていますが、Channel を使えば待ち合わせができるようです。

chann = Channel(Bool).new

spawn do
  10.times do
    print 'a'
    sleep 1
  end
  chann.send true
end

spawn do
  10.times do
    print 'b'
    sleep 1
  end
  chann.send true
end

chann.receive
chann.receive

Channel は Crystal のドキュメントに説明がなくてよくわからないのですが、Ruby の Queue みたいなもんでしょうか。

Channel を各 spawn ブロックの中で使えば sleep を使わなくてもコンテキスト切り替えができるようです。