0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Go言語における競合状態(Race Condition)の理解と対処法

Posted at

はじめに

Go言語(golang)は並行処理を簡単に扱えることで有名ですが、その分、競合状態(race condition)に注意する必要があります。
Go言語での競合状態を具体的なサンプルコードを用いて解説し、その対処法について説明します。

競合状態(Race Condition)とは?

競合状態は、複数の並行処理が同時に共有データにアクセスする際に発生する可能性があり、これが原因で不具合や意図しない動作が発生することがあります。

例えば、下記の競合状態です。

package main

import (
	"fmt"
	"sync"
)

var sharedCounter int

func main() {
	wg := &sync.WaitGroup{}
	mu := &sync.Mutex{}

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			mu.Lock() // コメントアウトして、競合状態を発生させる
			value := sharedCounter
			value++
			sharedCounter = value
			mu.Unlock() // コメントアウトして、競合状態を発生させる
		}()
	}

	wg.Wait()
	fmt.Println("sharedCounter:", sharedCounter)
}

sharedCounterという共有変数に1000個のゴルーチンがアクセスし、インクリメント操作を行います。sync.Mutexを使ってアクセスをロックしていますが、mu.Lock()mu.Unlock()の行をコメントアウトすると、競合状態が発生します。

競合状態の発生理由

競合状態が発生する理由は、複数のゴルーチンが同時にsharedCounterにアクセスし、その値を読み書きしているためです。これにより、一部のインクリメントが上書きされ、期待した結果と異なるカウント値が出力されることがあります。

対処法

競合状態を回避する方法として、sync.Mutexを使用して、同時に共有データにアクセスすることを制限します。この例では、mu.Lock()でロックを取得し、mu.Unlock()でロックを解放しています。これにより、一度に1つのゴルーチンだけが共有データにアクセスでき、競合状態が回避されます。

その他の対処法

競合状態への対処法としては、sync.Mutexの他にも、sync.RWMutex(読み込み専用のロックを許可)やチャネルを使用する方法もあります。チャネルは、ゴルーチン間でデータの送受信を行う仕組みで、明示的なロックを使わずに競合状態を回避できる場合があります。

package main

import (
	"fmt"
	"sync"
)

var sharedCounter int

func incrementCounter(wg *sync.WaitGroup, ch chan<- int) {
	defer wg.Done()

	ch <- 1
}

func main() {
	wg := &sync.WaitGroup{}
	ch := make(chan int, 1000)

	go func() {
		for i := 0; i < 1000; i++ {
			wg.Add(1)
			go incrementCounter(wg, ch)
		}
		wg.Wait()
		close(ch)
	}()

	for value := range ch {
		sharedCounter += value
	}

	fmt.Println("sharedCounter:", sharedCounter)
}

このコードでは、1000個のゴルーチンがチャネルchに1を送信し、メインゴルーチンがチャネルから受信してsharedCounterをインクリメントします。この方法では、競合状態が発生せず、期待通りの結果が得られます。

競合状態の検出

Go言語には、競合状態を検出するためのツールgo run -raceが付属しています。このツールを使うことで、コード内の競合状態を検出し、修正することができます。

まとめ

Go言語での競合状態は、複数のゴルーチンが同時に共有データにアクセスする際に注意が必要です。対処法としては、sync.Mutexやチャネルを利用することで、競合状態を回避できます。また、go run -raceを用いることで、競合状態を検出しやすくなります。これらの手法を使って、安全な並行処理を実現しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?