고에서 맵은 기본적으로 스레드 안전하지 않습니다. 여러 개의 고루틴이 같은 맵에 동시에 쓰기 또는 읽기를 수행하면 데이터 경합이 발생하여 fatal error: concurrent map read and map write라는 패닉이 발생하거나 데이터가 손상됩니다.
맵에 대해 스레드 안전하게 작업하려면:
sync.Map 패키지를 사용할 수 있습니다.var mu sync.RWMutex m := make(map[string]int) // 쓰기 mu.Lock() m["key"] = 1 mu.Unlock() // 읽기 mu.RLock() v := m["key"] mu.RUnlock() // 또는 sync.Map var sm sync.Map sm.Store("key", 1) v, _ := sm.Load("key")
고에서 맵에서 서로 다른 고루틴이 동시에 읽고 쓰는 것이 왜 그렇게 위험한가요? 맵은 내장 타입이 아닙니까?
답변: 고에서 내장 타입인 맵은 동기화를 제공하지 않습니다. 여러 고루틴이 동시에 맵에 접근하면 잘못된 값이 나올 뿐만 아니라 프로그램이 중단될 수 있습니다. 심지어 (겹치는 키가 없을 때도) 동시에 읽고 쓰는 것조차 치명적인 오류를 초래할 수 있습니다. 이는 일부 다른 언어와 다르며, 그 언어에서는 컬렉션이 동시 접근에 대해 '관대하다'고 볼 수 있습니다.
이야기
실제 프로젝트에서 개발자는 데이터 캐싱을 위해 글로벌 맵을 사용했습니다. 서비스는 테스트에서는 안정적으로 작동했지만 부하 테스트와 프로덕션 환경에서 fatal error: concurrent map read and map write 오류로 중단되었습니다. 그 원인은 Mutex 없이 서로 다른 HTTP 요청에서 맵에 대한 병렬 접근이었습니다.
이야기
Go 웹 애플리케이션에서 한 개발자는 성능을 개선하기 위해 일반 맵을 커넥션 풀로 사용했으며, 프레임워크가 다중 작업을 처리한다고 생각했습니다. 트래픽이 급증하자 서비스가 명백한 이유 없이 중단되었고, 데이터 경합으로 인해 맵의 데이터가 손상되어 패닉이 발생했습니다.
이야기
Go 내부 서비스에서 애플리케이션은 오로지 쓰기 전용이라고 가정하고 통계 수집을 위해 맵을 사용했습니다. 실제로는 코드의 다른 부분이 정기적으로 데이터를 요청하여 보고서를 생성했으며, 이는 하루에 한 번만 발생하는 잡기 힘든 중단을 초래했습니다 — 바로 통계가 실행될 때입니다. 분석 결과, 읽기 및 쓰기가 어떤 차단 없이 겹쳤다는 사실이 드러났습니다.