[Go 공식문서 한국어 정리] ⑯31. Go 메모리 모델
[Go 공식문서 한국어 정리] ⑯31. Go 메모리 모델
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 서론
이 문서는 한 고루틴에서 변수를 쓰고 다른 고루틴에서 서로 쓴 값을 읽을 때 어떤 조건에서 해당 값이 보장되는지를 정의합니다.
2. 핵심 개념
• 데이터 레이스(data race): 동일한 메모리 위치에 대한 쓰기가 다른 읽기나 쓰기와 동시에 발생하는 것. sync/atomic을 제외.
• DRF-SC: 데이터 레이스가 없는 프로그램은 순차적으로 일관되게 실행된다(sequentially consistent).
• happens-before: 순서와 동기화 관계를 통해 정의되는 부분 순서. 이동적 싫범의 상위집합.
• 가장 중요한 조언: “다른 문서를 읽어야 풀 수 있다면 너무 단순하게 작성하는 것이다.”
3. 주요 내용 상세
3.1 동기화
• 초기화: 모든 init함수의 완료는 main.main의 시작보다 앞에서 발생. q의 init이 p의 init보다 앞에 실행되며 p가 q를 import하는 경우.
• 고루틴 생성: go 문을 실행하는 것이 새 고루틴의 시작보다 앞에서 발생.
• 고루틴 종료: 고루틴의 종료는 다른 이벤트보다 앞에 발생한다는 보장이 없습니다.
3.2 채널 통신
• 송신(send)은 수신(receive)보다 앞에서 발생. 좌표: send → receive
• 버퍼되지 않은 채널에서는 수신이 송신보다 앞에서 발생. 좌표: receive → send
• 채널 닫기는 0을 반환하는 수신보다 앞에서 발생.
• 용량 C인 채널의 k번째 수신은 k+C번째 송신보다 앞에서 발생. 이를 이용한 세마포어의 구현이 가능합니다.
3.3 무역화
• sync.Mutex: n번째 Unlock은 m번째 Lock(m>n)보다 앞에서 발생.
• sync.RWMutex: RLock은 이전의 Unlock보다 앞에서 발생하고 RUnlock은 다음 Lock보다 앞에서 발생.
• sync.Once: f()의 완료는 모든 once.Do(f)의 반환보다 앞에서 발생.
3.4 원자적 연산
• sync/atomic의 원자적 연산은 모든 고루틴에서 순차적인 일관성으로 실행되는 것처럼 동작합니다.
• 원자적 연산 A가 B에의해 관찰되면 A은 B보다 앞에서 발생.
3.5 잘못된 동기화 예시
• 더블 체크 라커딩: done 플래그를 관찰한다고 해서 a의 쓰기가 보장되지 않습니다.
• 바쁘 대기: done이 true가 되어도 a의 값이 보장되지 않을 수 있습니다.
• 제대로: 명시적인 동기화 메커니즘을 사용해야 합니다.
3.6 컴파일러 제한
• 컴파일러는 원본 프로그램에 없는 쓰기를 산입하면 안됩니다.
• 핚 번의 읽기가 여러 값을 관찰하도록 하면 안됩니다.
• 핚 번의 쓰기가 여러 값을 쓰도록 하면 안됩니다.
• 이러한 제한은 C/C++의 최적화를 공유하는 컴파일러에서도 적용되어야 합니다.
4. 실전 활용
• 다중 고루틴이 동시에 데이터를 접근할 때는 벅어(channel)나 무역화 원시전(sync, sync/atomic)를 사용합니다.
• go build -race를 사용해 데이터 레이스를 검증합니다.
• “단순하게 작성하라”는 데이터 레이스를 피하는 가장 좋은 방법입니다.
5. 정리
• Go의 메모리 모델은 데이터 레이스가 없는 프로그램에 대해 순차적인 일관성을 보장합니다.
• 동기화는 채널, 무역화, 원자적 연산, 그리고 sync.Once를 통해 달성됩니다.
• 잘못된 동기화는 예상치 않은 결과를 초래할 수 있으므로 방지해야 합니다.
• 컴파일러는 데이터 레이스를 산입하지 않도록 주의해야 합니다.
#Go #Golang #MemoryModel #Concurrency #동시성 #공식문서

오뉴노노 님의 최근 댓글
ㅋㅋㅋㅋㅋ 2019 01.14 잘 읽었습니다 2018 12.30 포인트가 없어서 아직 시작을 못하고있는데요! 글은 잘 읽었습니다! 포인트 쌓고 도전할거에요 2018 12.30