メモと動作確認したソースコードはgithubにありますのでごらんください。
では、続きです。
その14
APIで非同期にするのは避けよう!
- 順番にAPIを利用したい時はどうすればいいのか?
- 同期APIを用意すると、並列実行が簡単に呼び出せる。
- (※runtime.GOMAXPROCS(2)とかすると、順番がおかしくなるが、呼び出し順をどうするか?という議論ではない!)
ここでの議論は、API側で非同期の処理を実装するのではなく、同期APIにしておいて、非同期で実行させるかどうかは呼び出し側の都合で変えられるようにしておくのがいいということでしょう。
その15
goroutineと通信するための構造体のメンバのChannelか、単体のChannelを使え!
Channelの動作について(何度も復習するところですが、私の認識は以下です。)chanのバッファが0の時は、他のGoroutinesがChannelを受け取らないとGoroutineがブロックされる。chanのバッファが1の時は、1個はバッファに置けるので、Goroutineがブロックされず、突き抜ける。
goroutineを使った時のgoroutine間の通信をどうするか?という話題で、channelを使って、データのやりとりを行えばいいということです。
で、ここで議論になったのは、以下のコードです。(抜粋ですので、全体は原著の資料か、githubの資料をごらんください。
func (s *Server) Stop() { fmt.Println("server stopping") s.quit <- true <-s.quit fmt.Println("server stopped") }
s.quit<-trueをして、すぐに、<-s.quitでChannelから値を取り出しているように見えますが、Channelはバッファ0のChannelですので、s.quit<-trueした時点で、goroutineはブロックされます。(他のgoroutineでChannelから抜き取るまで。私はスレッドがブロックされるという表現ではなく、goroutineの切り替わりと言っています。)したがって、別のgoroutineが終了処理を行って、完全に終了した後に<-s.quitが実行されることになります。
その16
Buffered Channelを使って、goroutineのリークを避けよう!
- 何度も繰り返しになりますが、channelに書き込むと、goroutineはブロックされます。
- goroutineはchannelの参照を握っているので、channelがガベージコレクトされることはないことになります。
- goroutinesの切り替わりは「明示的なブロック」で発生するので、今回のようにバッファがないchannelだと、goroutineの切り替わりが発生するので、goroutineの処理が終わる前にchannelの受信待ちが終了してしまうことがある。資料の例だとlocalhost:8080がエラー応答になった時に、エラーを検知して終了する。しかし、google.comの処理がまだ残っているので、結果としてリークしたことになる。
その17
quit chanを使って、goroutinesのリークを避けよう!
close(quit)を行うと、channelに受信しない事が明示されます。その16と同じように、先にエラー応答が発生した時にそのまま終了するのではなく、close(quit)することで、登録されているgoroutineはすべて、case: <-quit:が実行されます。したがって、全て、終了させる事ができます。
goroutineの切り替わりを意識したコードを書くことで、channelのリークを防ぐことができるのでしっかり意識しましょう。
chan(struct{})はクローズ(終了)用なので、誰もChannelに送信できないようにするために、空の構造体にしているものと思われます。
次回は明確にテーマを決めなかったのですが、何かやります。
0 件のコメント:
コメントを投稿