프로그래밍Go 개발자

Go에서 map을 사용하면서 다중 스레드 접근과 반복 중 요소 삭제로 인한 암묵적인 오류를 피하기 위해 알아야 할 세부 사항은 무엇인가요?

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

답변.

Go에서 map은 기본적으로 스레드 안전하지 않은 데이터 구조입니다. 여러 고루틴에서 동시에 map에 쓰거나 수정하면 경쟁 상태와 "concurrent map writes"와 같은 패닉이 발생합니다. map을 보호하기 위해 일반적으로 sync.Mutex, sync.RWMutex 또는 표준 라이브러리의 특별한 유형인 sync.Map을 사용하여 안전한 원자적 작업을 구현합니다.

map에서 요소를 삭제하려면 delete(map, key)를 통해 할 수 있으나, map을 반복하는 동안 (즉, range를 사용할 때) 다음과 같은 주의사항이 있습니다:

  • 현재 반복 중인 요소는 안전하게 삭제할 수 있습니다.
  • range 반복 중에 새 요소를 추가할 수는 있지만, 새로운 키가 현재 반복에서 처리된다고 보장되지는 않습니다.
  • 반복 중에 다른 고루틴에서 map을 수정하는 것은 오류입니다 (경쟁 상태 또는 패닉).

스레드 안전한 map 작업의 예:

type SafeMap struct { mu sync.RWMutex m map[string]int } func (s *SafeMap) Load(key string) (int, bool) { s.mu.RLock() v, ok := s.m[key] s.mu.RUnlock() return v, ok } func (s *SafeMap) Store(key string, value int) { s.mu.Lock() s.m[key] = value s.mu.Unlock() }

속임수 질문.

질문: 동일한 map에 대해 range 반복하는 동안 안전하게 요소를 삭제할 수 있습니까?

답변: 네, 현재 반복 중인 키를 삭제하는 경우에만 가능합니다. 그러나 다른 고루틴에서 map을 동시에 수정하는 것은 허용되지 않으며, 이는 경합을 초래합니다.

m := map[string]int{"a": 1, "b": 2, "c": 3} for k := range m { delete(m, k) // 안전합니다! (이 고루틴에서만 수행하는 경우) }

주제에 대한 세부 사항을 몰라서 발생한 실제 오류의 예.


이야기

하나의 모니터링 서비스는 여러 고루틴에서 (메트릭 카운터) map에서 통계를 기록하고 업데이트되었습니다. 피크시점에 "concurrent map writes" 패닉이 발생했고, 서비스가 중단되어 데이터가 손실되었습니다. 해결책: mutex를 추가하거나 일반 map 대신 sync.Map을 사용했습니다.


이야기

데이터 마이그레이션 과정에서 누군가가 병렬 고루틴을 사용하여 큰 map의 지우기를 가속화하려고 했고, 각 고루틴이 자신의 키를 range를 통해 삭제하고 있었습니다. 결과적으로 지속적인 데이터 경합과 예측할 수 없는 크래시가 발생했습니다. 마이그레이션 후에는 순차적으로 지우기로 돌아가거나 range 동안 접근을 차단해야 했습니다.


이야기

map에서 range 루프를 돌리는 개발자가 같은 map에 새 데이터를 추가했습니다 (예: 그래프의 인접 노드 목록을 형성). 그 결과 새로운 키가 현재 range 통과에서 무시될 수 있어 그래프가 완전히 처리되지 않았습니다. 이 버그는 드문 케이스에 대한 전면 테스트에서만 발견되었습니다. 수정 후 알고리즘이 추가 대기열을 사용하여 다시 작성되었습니다.