グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



goroutineでmapにアクセスするときは排他制御をしよう

Goではgoroutineを使って簡単に並行処理を書けますが、何気ないところでエラーが発生してしまうことが(筆者は)度々あります。
さらに偶然にも正常に処理が完了し、何回かに1回しか失敗しないケースの場合、なかなか気付かないこともあります。

今回はmapの競合で発生したエラーと、エラーを起こさないための排他制御の使い方を見ていこうと思います。

エラーが発生する原因

まずは意図的にエラーを発生させてみます。

goroutineの外で定義したmapに、複数のgoroutineから書き込みを行います。

正常に終了することもありますが、エラーが発生した場合の出力結果は以下の通りです。

1つのgoroutineがmapに書き込んでいる場合、他のgoroutineが同時に読み書きすることができないので、panicが発生しました。

Go1.6のリリースノートにも以下のような記述がありました。

if one goroutine is writing to a map, no other goroutine should be reading or writing the map concurrently. If the runtime detects this condition, it prints a diagnosis and crashes the program.Go 1.6 Release Notes

sync.Mutexで排他制御をかける

先ほどエラーが発生したコードに排他制御を追加したところ、今度は何回実行しても正常に処理が終了します。

mapにアクセスする前にロックを取得するため、競合することが無くなりました。

sync.Mapを使ってみる

先ほどはsync.Mutexを使って自分で排他制御をしましたが、sync.Mapを使えば自分で排他制御を書かなくても競合しないので、手軽に使用できそうです。

まずはMapの構造体と、値を追加するStoreメソッドの実装を覗いてみましょう。(一部省略)

この中で Lock/Unlock が行われているので、メソッドを使う側ではそれを意識しなくて済むようになっています。

それでは、sync.Mapを実際に使ってみたいと思います。

排他制御を自分で書かなくても、並行処理でmapへ書き込みができました。

ロックを意識せず直感的に使用できる点では非常に便利ですが、key,valueに型の制約がないため、多少の使いずらさを感じました。

まとめ

今回はgoroutineで同一のmapにアクセスする方法を検証しました。

方法はいくつかありましたが、並行処理で同一のmapや変数にアクセスする場合はいずれかの方法で排他制御が必要になります。
偶然処理が正常に完了してしまうこともあるので、goroutineを使用する際は十分に検証する事が大切かと思います。

また、今回紹介していませんがsync.RWMutex では読み取り用のロックもあるので、実装に応じて検討してみてください。

The following two tabs change content below.

アーティス

創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします