Go by Example: 뮤텍스

이전 예제에서 우린 atomic operations을 사용해 간단한 카운터 상태를 관리하는 방법을 보았습니다. 좀 더 복잡한 상태에 대해서는 여러개의 고루틴이 데이터에 안전하게 접근할 수 있게 [뮤텍스(mutex)](http://en.wikipedia.org/wiki/Mutualexclusion)_를 사용할 수 있습니다.

package main
import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)
func main() {

우리 예제에서 상태(state)는 맵입니다.

	var state = make(map[int]int)

mutexstate에 대한 접근을 동기화합니다.

	var mutex = &sync.Mutex{}

읽기와 쓰기가 얼마나 이루어지는지를 추적할겁니다.

	var readOps uint64 = 0
	var writeOps uint64 = 0

100개의 고루틴을 띄워 각 고루틴에서 1 밀리초마다 반복적으로 읽기를 실행합니다.

	for r := 0; r < 100; r++ {
		go func() {
			total := 0
			for {

각 읽기마다 접근을 위한 키값을 선택합니다. state에 상호 배제 접근을 보장하기 위해 mutexLock()한 뒤 키값으로 값을 읽고, 뮤텍스를 Unlock()한 다음 readOps 카운트값을 증가시킵니다.

				key := rand.Intn(5)
				mutex.Lock()
				total += state[key]
				mutex.Unlock()
				atomic.AddUint64(&readOps, 1)

읽기 사이를 조금 대기합니다.

				time.Sleep(time.Millisecond)
			}
		}()
	}

읽기에서와 같은 패턴으로 쓰기를 시뮬레이션 하기 위해 10개의 고루틴을 띄웁니다.

	for w := 0; w < 10; w++ {
		go func() {
			for {
				key := rand.Intn(5)
				val := rand.Intn(100)
				mutex.Lock()
				state[key] = val
				mutex.Unlock()
				atomic.AddUint64(&writeOps, 1)
				time.Sleep(time.Millisecond)
			}
		}()
	}

1초간 statemutex에서 10개의 고루틴 작업을 돌립니다.

	time.Sleep(time.Second)

최종 연산 횟수를 리포팅합니다.

	readOpsFinal := atomic.LoadUint64(&readOps)
	fmt.Println("readOps:", readOpsFinal)
	writeOpsFinal := atomic.LoadUint64(&writeOps)
	fmt.Println("writeOps:", writeOpsFinal)

state의 최종 잠금(lock)으로, 어떻게 끝났는지 보여줍니다.

	mutex.Lock()
	fmt.Println("state:", state)
	mutex.Unlock()
}

프로그램을 실행하면 동기화된 mutex-동기화된 state에 대해 약 총 90,000번의 연산이 실행되었음을 보여줍니다.

$ go run mutexes.go
readOps: 83285
writeOps: 8320
state: map[1:97 4:53 0:33 2:15 3:2]

다음 장에선 동일한 상태 관리 작업을 고루틴과 채널만을 가지고 구현하는 방법을 살펴보겠습니다.

다음 예제: 상태있는 고루틴.