今回から、「第2部 並行 Haskell」に入りました。
個人的な感覚としては、並列の話は馴染みがない話で難しく感じることが多かったですが、並行の話は馴染みがある話で楽に読みすすめられたように思います。並行は要するにマルチスレッドプログラミングの話なので、スマートフォンアプリエンジニアとしては親しみやすいです。
7章 並行制御の基本:スレッドと MVar
新しいスレッドの生成は簡単で、forkIO
に IO
を渡すだけです。
7.1
スレッドを生成して、一定時間後に printf
を行うという例です。一定時間待つのは threadDelay
で行います。
7.2
スレッド間でデータをやり取りするには、MVar
を使います。これは、値がひとつだけ入る箱として使います。takeMVar
は値を取り出しますが、値が入るまでブロックします。putMVar
は値を入れますが、値が入っていればブロックします。
ちなみに、実行時にデッドロックを検知する仕組みがあって BlockedIndefinitelyOnMVar
という例外が投げられるようです。なにそれべんり。詳しいことは後の章で出てくるとのこと。
7.3
別スレッドでログサービスを動かす例です。ログメッセージを渡す手段として MVar
を使います。
また、メインスレッド終了前にログサービスを終了する仕組みも実現しています。MVar
で終了コマンドを送り、別の MVar
でサービス終了を待ちます。
7.4
スレッド間共有データのリードライトロックに MVar
を使う例です。共有データを MVar
で包んでやることで簡単に実現できてしまいます。
ただし、Haskell の遅延評価には注意が必要です。未評価の値を putMVar
すると、ロックは一瞬ですみますが、それが大量に行われると未評価の値の連鎖になってスペースリークの原因になります。一方、putMVar
の時に評価を行うようにすると、スペースリークの問題は解決しますが、ロック保持が長くなります。そこで、未評価の値を putMVar
した後で seq
を使って値を評価する、とやるとうまくいきます。
7.5
MVar
を組み合わせてデータ構造を作る例です。
無制限バッファ Chan
を MVar
で作ることができます。writeChan
と readChan
はわりと簡単にそれっぽく作ることができます。
しかし、Chan
を複製する dupChan
をそのノリで作ると、元のチャネルと複製したチャネルを両方読み出そうとしたときにデッドロックが発生してしまいます。そこで、take
してすぐ put
する readMVar
が登場します。これを使うと dupChan
も実現できます。
ただし、残念ながら unGetChan
(読み出し点に値を戻す)はうまく実現できません。
7.6
MVar
は CPU 時間の割り当ての意味で公平なスケジューラであるという話が出てきます。ただ、ここの話は分かるような分からないような、ちょっと微妙な感じがしました。
8章 入出力の重ね合わせ
アクションを非同期に実行してその結果を待つ async
/ wait
が、forkIO
と MVar
で簡単に実現できるという話が最初に出てきます。
この async
の中で getURL
を実行することで、非同期にサーバからデータ取得を行います。
getURL
は様々な要因で失敗する場合があるわけですが、そのエラー処理をするために 、Haskell の例外を使うという話になります。
8.1
Haskell に例外なんてあったんだ、という気分ですが、実際のところ組み込みの構文としてはなくて、ライブラリ関数です。Exception
型クラスというのがあります。
throw
と catch
で例外を「投げる」「捕捉する」という処理が実現されています。他にも、try
handle
onException
throwIO
bracket
finally
なんてのもあります。
8.2
例外について知ったわけですが、getURL
は例外を投げます。これを捕捉するような処理をかけば、エラー処理が実現できます。
8.3
複数の getURL
を実行して、そのうちのどれかが終了するのを待つ、という例です。waitEither
や waitAny
でできます。
最後に
今回は珍しく2章分を一気に読んでしまいました。内容がそれほど難しくなかったせいもあるかと思います。次回は9章から。