Mutexes

Mutexes->뮤텍스 #

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

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

func main() {

	// 우리 예제에서 `상태(state)`는 맵입니다.
	var state = make(map[int]int)

	// `mutex`는 `state`에 대한 접근을 동기화합니다.
	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`에 상호 배제 접근을 보장하기 위해 `mutex`를`Lock()`한 뒤 키값으로 값을 읽고, 뮤텍스를 `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초간  `state`와 `mutex`에서 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]

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