[Go言語]数値を安全にカウントアップする方法

こんにちは。KOUKIです。

Go言語のGoroutineを使ったプログラムを書く時に、「思った通りの挙動をしないな」と思う時があります。

その代表例が数値のカウントアップです。

事例: 数値が安全にカウントアップしない

まずは、事例をみてください。

上記のコードでは、10000個のGoroutineを生成し、counter変数に対して各自10000回のカウントアップを行なっています。

以下のコマンドでプログラムを複数回実行してみます。

ご覧の通り、カウンターの値がバラバラになってしまいました。これは、Goroutineによる変数への書き込みを行う際、変数(メモリ)の排他制御をしていないので、ほぼ同時に値を書き込むことによって、正しい値が変数に保持されないことによって起こります。

対策1: Mutexを使う

このような場合は、排他制御を行うことができるsync.Mutexを使います。これを使えば、変数にアクセスできるGoroutineは常に一つだけ、という状態を作れます。

couter変数をカウントアップするfor文の前にMutexでロックをかけました。これで排他制御が完了です!

プログラムを実行してみましょう。

今度は、counterの値がブレてないですね!

対策2:Atomicを使う

Mutexより手軽に排他制御を行う方法があります。それが、sync.Atomicです。

先ほどと違って、「atomic.AddUint64(&counter, 1)」のみで排他制御を行いました。

プログラムを実行してみます。

今度も問題なくカウントできているみたいですね。しかし、こっちの方がかなり時間がかかるな。。

他のブログや資料を見るとatomicの方が処理時間が早いみたいなんですが、私の環境だとAtomicがめちゃくちゃ時間かかってますね。。

おわりに

MutexやAtomicを使うと排他制御が楽です!ガンガン使っていきましょう!!

それではまた!

関連書籍