2013年12月23日月曜日

Golang Cafe #9を開催しました。

Golang Cafe #9を開催しました。

今回は、「Go言語でデザインパターン」ということで、結城浩さんの書籍「Java言語で学ぶデザインパターン入門 マルチスレッド編」を、2月頃にGoで書いたものがあったので、それを元に進めてみました。
githubのリポジトリにタイトルのままのディレクトリが作られているのでそちらをごらんください。

最初の「Single Threaded Execution」では、本来は、排他制御を行わないと、変数の値が正しく読み取れない(他のスレッドでの変更が中途半端に反映された)状態が発生して、「Broken」という出力がある想定だったのですが、Go on Mac OSXだと、何もしなくても、全く不整合が発生せず、その時の状況を忘れてしまっていたので、3人でハマってしまいました。(Windowsでは、不整合が発生したので排他制御を入れなければいけなかったが、Ubuntuでは全く発生しなかった)
恐らく、コンパイル時に最適化されてしまったのかな?と後になって考えるところです。
Goroutineの切り替わりが問題か?と思って、runtime.GOMAXPROCS(3)とかも適用してみたのですが、全く不整合が発生しませんでした。
該当コードはこちらです。(channelで排他制御のコードがありますが、全く無意味 http://play.golang.org/p/gz21HM20kv

本来、ここで言いたかった事は、「しっかり排他制御を行って、同時にアクセスできるのは1つのスレッドのみ」ということを実現しましょう。ということでした。

次の「Immutable」は、複数のスレッドで実行しても、値を変更するAPIを用意しなければ、変更したくても変更できないというパターンです。
特に問題が発生しなかったので素通りしました。

最後に、「Guarded Suspention」はchannelはスレッドの終わりを待つだけのものなので、特に注目するところではないのですが、今回は、sync.condを使って排他制御を行っています。(これが正しいかどうかは別として、書籍のコードと同じような事をしようと思うと恐らくこれだろうと思っています)
理由としては、C#でいうところの、Monitor.Wait()とMonitor.Pulse()を実現する方法がChannelでは不可能だからです。(でも、実際には同じような事はバッファリングすればできる…か?)
プログラムの内容としては、ClientスレッドがQueueにPutするまで、Serverスレッドは待ち続ける。という内容になっていて、スレッドの動作はバラバラですから、Queueにデータがある間はsync.cond.L.Lock()でロックし、値を取り出し、キューから削除してUnlock()しますが、データが無くなった時には、sync.cond.L.Wait()でキューにデータがセットされるのを待ちます。
Clientスレッドでは、キューにデータをセットしたら、sync.cond.L.Signal()を呼び出して、サーバ側にデータが入った事を伝えています。(その後は、CPUが勝手にスレッドを動かすはずなので、どのスレッドでもどうぞ。ということで)

ここで、問題になったのが、init()の呼び出しについて。
init()はパッケージが読み込まれた時に、1度だけ実行される特殊な関数ですが、構造体のメソッドとしてinit()を定義していても呼び出されるのか?という話になりました。
そこで、最初は「呼び出される」(static{}と同じ動き)と言っていたのですが、ドキュメントにそのような記載はないし、よくよくコードを確認すると、main()に呼び出しがあったので、メソッドにした場合は、呼び出される事はありません。(コメントアウトしてみると、panicになりました)

というところで、2時間が経過したので、ここまでとなりました。

次回は、12/29(日)に10回目を行います。
テーマは「imageパッケージと戯れる」にする予定です。