프로그램에서 Deadlock의 발생을 catch하기 어려운 경우가 종종 있다, 하지만 Deadlock의 발생은 아주 치명적이기에 Address를 넘기는 경우 면밀히 코드를 확인 하는 것이 좋다. 이러한 실수를 미연에 방지하고자 golang에서는 tool을 기본적으로 제공한다.
Data Race Detector는 두개 이상의 goroutine에서 같은 변수를 동시에 접근하는 경우가 있는지를 확인 한다.
아래의 코드를 실행 해 보자.
func main() {
c := make(chan bool)
m := make(map[string]string)
go func() {
m["1"] = "a" // First conflicting access.
c <- true
}()
m["2"] = "b" // Second conflicting access.
<-c
for k, v := range m {
fmt.Println(k, v)
}
}

Err가 없이 정상적인 출력을 보여 준다. 그렇다면 같은 코드를 Data Race Detector 옵션으로 실행 시켜 보자.
$ go test -race mypkg // to test the package
$ go run -race mysrc.go // to run the source file
$ go build -race mycmd // to build the command
$ go install -race mypkg // to install the package
#나의 경우
$ go test -race <file_name.go>

터미널에 보이듯이 FAIL 했다. 상세부분에서는 goroutine, err line 정보를 확인 가능하다. 위의 코드에서는 map에 데이터를 넣는 부분에서 문제가 발생하는 것을 err log를 통하여 확인이 가능하다. 동시에 접근하는 곳에는 mutex.lock으로 한번에 하나씩만 접근 가능하게 만들 수 있다.
동시에 접근하는 데이터를 찾았다면, 이제 lock을 사용하여 한번에 하나씩 접근하게 변경 하였을 것 이다. 이제 mutex.lock이 얼마나 시간을 소비하고 있는지 효율적인지 확인 해 보자.
type lockMap struct {
data map[int]string
mu sync.Mutex
}
func createDate() *lockMap {
return &lockMap{
data: make(map[int]string),
mu: sync.Mutex{},
}
}
func (m *lockMap) Read(key int) string {
m.mu.Lock()
defer m.mu.Unlock()
return m.data[key]
}
func (m *lockMap) Write(key int, value string) {
m.mu.Lock()
time.Sleep(1 * time.Second)
defer m.mu.Unlock()
m.data[key] = value
}
func TestMutexLock(t *testing.T) {
m := createDate()
go func() {
m.Write(1, "1")
}()
m.Write(2, "2")
}
위 코드는 Race Detector로 구동 시켜도 문제가 없다. 그렇기에 이제 pprof를 이용해 보자. consol에 아래의 명령어를 입력 한다.
$ go test -mutexprofile mutex.prof
$ go tool pprof mutex.prof


그래프의 내용에서 Write function 내의 unlock의 시간의 소비가 큰 것을 명확하게 보여주고 있다. 이는 위 코드에서 의도적으로 time.Sleep(1 * time.Second) 하는 부분이다.
우선 lock을 사용 여부의 결정에 Race Detector를 활용하여 도움을 받을 수 있으며, 해당의 lock이 효율적인지를 판단하기 위하여 pprof를 이용한다면 좀 더 견고한 프로그램을 작성할 수 있는 것이다.