Haskell による並列・並行プログラミング #14 12章 並行ネットワークサーバ
担当:lotzさん
多クライアントと通信するサーバアプリケーションの構築
12.1 簡単なサーバ
forever でループ、コネクションが発生したら対話をfork
forkFinallyを使って終了処理を書く(ハンドラを閉じる)
-threadedをつけて、+RTS -N でマルチコアで利用できる
軽量スレッドがなければイベントループを書くことになる。大変
12.2 単純なサーバの状態による拡張
設計1:1つのジャイアントロック
単一のMVarで状態を管理する
現在の係数(サーバーの状態)
クライアントのハンドラリスト
シンプルだが、クライアントがロックを取得しないと対話できない
クライアントが増えると困る
設計2:サーバスレッドごとに1つのチャネル
クライアントからのメッセージはチャネルに書き込まれる
係数の変更とクライアントへのメッセージの送信は不可分→ロックの保持が必要
設計3:ブロードキャストチャネルの利用
グローバルステートのチャネルをdupChanして各サーバスレッドに配る
ブロードキャストチャネルからのメッセージを監視するスレッド・クライアントからのメッセージ受信スレッド・サーバスレッド本体の3つが必要になる(1クライアント毎に)
設計4:STMの利用
ブロードキャストチャネルを用意するのではなく,TVarを用意して書き込んでもらう
チャネルの監視の代わりに retry を使う
監視スレッドが要らなくなる
実装
サーバースレッドはTVar(係数)の変化かTChan(メッセージ)のどちらか早い方を待つ→ race を使う
12.3 チャットサーバ
talk ではSTMを使わずにmaskを使って非同期例外安全に書こうとしてるのはなんでだろう
IOアクションを含めた処理なので、STMに閉じていない
一方で、maskしてあげて、例外時には生成したクライアントを削除してやらなければ、ゾンビクライアントがサーバーが管理するクライアントリストの中に残ってしまう(メモリリーク)
runClient: 自分向けのサーバー処理か、メッセージ受信をraceで受ける
今回の実装では非有界なTChanを使っているので、メッセージ数が多くなりすぎたときにreadTVarのO(n^2)が重くなる
次回: 13章 スレッドを用いた並列プログラミング
担当: wado さん