アペフチ

A Tour of Goのエクササイズをやってみた

A Tour of Goのエクササイズをやって、GitHubに上げてみた:github.com/KitaitiMakoto/a-tour-of-go-exercises

ウェブクローラーの課題(exercise-web-crawler.go)が難しくて、まずgoroutineを使って非同期にクロールさせるのに手こずった。一つのHTTPリクエストに一つのgoroutineを割り当てた時、終了の待ち合わせはどうするのがいいんだろう。僕は、一々チャンネルを閉じるようにした。閉じるのの待ち合わせは

for range ch {}

とやった。この他に、go呼び出しの辺りにラベルを付けて、groutineから戻ったところでbreakするというやり方もあるようだ。エクササイズは本当は教師がいて答え合わせしてもらえるととてもいいのだけど、残念ながらいないので、誰か、「こうしたらもっといいよ」というの教えてください。まあ、色んなソースを読むというのが、よいのだろうとは思うが。

次に、「一度フェッチしたURIは二度フェッチしないようにする」というのも課題の一部で、ヒントに「その管理にマップを使うのはいいけど、マップは単独では並行処理に関して安全ではない」とあって、この排他制御にもちょっと困った。大きなロックを獲得して、その中でフェッチすると、並行処理させている意味が無くなっちゃう。でも、どのタイミングでロックを取ればいいのか、何のロックを取ればいいのか、というのに迷った。「一つのURIにつき一つのミューテックスを作る」ということを最初考えたのだけど、そもそもあるURIに対応するミューテックスが存在するかの確認処理と、その後ミューテックスを作るまでの間に他のgoroutineが同じ物を触る可能性があるわけで、うまくいかない。結局、単純にマップその物をロックするようにした。そうすると、deferを使わない実装になってしまったのだけど、もっといいやり方がないものだろうか。