프로그래밍백엔드 개발자

고에서 맵으로 작업할 때 데이터 경합(data race)을 방지하는 메커니즘을 설명하세요. 어떤 보장이 있으며, 보호 없이 여러 개의 고루틴(goroutine)으로 맵을 단순히 작업하는 것이 왜 오류를 초래합니까?

Hintsage AI 어시스턴트로 면접 통과

답변

고에서 맵은 기본적으로 스레드 안전하지 않습니다. 여러 개의 고루틴이 같은 맵에 동시에 쓰기 또는 읽기를 수행하면 데이터 경합이 발생하여 fatal error: concurrent map read and map write라는 패닉이 발생하거나 데이터가 손상됩니다.

맵에 대해 스레드 안전하게 작업하려면:

  • 모든 읽기 및 쓰기 작업을 보호하기 위해 sync.Mutex 또는 sync.RWMutex를 사용해야 합니다.
  • 또는 고부하 경쟁 시나리오에 적합한 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 내부 서비스에서 애플리케이션은 오로지 쓰기 전용이라고 가정하고 통계 수집을 위해 맵을 사용했습니다. 실제로는 코드의 다른 부분이 정기적으로 데이터를 요청하여 보고서를 생성했으며, 이는 하루에 한 번만 발생하는 잡기 힘든 중단을 초래했습니다 — 바로 통계가 실행될 때입니다. 분석 결과, 읽기 및 쓰기가 어떤 차단 없이 겹쳤다는 사실이 드러났습니다.