[Go 공식문서 한국어 정리] ⑤7. Codewalk: Share Memory By Communicating
[Go 공식문서 한국어 정리] ⑤7. Codewalk: Share Memory By Communicating
원문: https://go.dev/doc/codewalk/sharemem/
#Go #Golang #Concurrency #Channel #Goroutine #Codewalk #공식문서
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. 서론
이 문서는 Go의 핵심 철학인 "메모리를 공유하여 통신하지 말고, 통신하여 메모리를 공유하라(Don't communicate by sharing memory; share memory by communicating)"를 실제 코드 예제를 통해 설명하는 Codewalk입니다. 채널을 통해 데이터의 소유권(ownership)을 옮기는 방식으로 동시성 문제를 해결하는 방법을 보여줍니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. 핵심개념
• Go의 동시성 철학: 채널을 통한 메모리 공유
• 소유권(ownership) 개념: 포인터를 채널로 전달하면 데이터의 소유권이 함께 이동
• 공유 자원(Resource)과 상태(State) 타입
• 다중 고루틴 간 채널 기반 협업
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3. 주요내용상세
3.1 프로그램 개요
이 프로그램은 URL 목록을 주기적으로 폴(poll)하여 HTTP 응답 코드를 확인하고, 각 URL의 현재 상태를 주기적으로 출력하는 간단한 프로그램입니다. 다음과 같은 고루틴들이 협력합니다:
• Poller 고루틴: URL을 폴하고 상태를 반환
• StateMonitor 고루틴: 모든 URL의 상태를 추적 및 출력
• main 고루틴: 리소스를 관리하고 지연 시간 조정
3.2 Resource 타입과 소유권 이동
Resource 타입은 폴할 URL과 마지막 성공 이후 에러 횟수를 저장합니다. 프로그램 시작 시 각 URL에 대해 하나의 Resource가 할당됩니다. main 고루틴과 Poller 고루틴은 Resource 포인터를 채널로 서로 주고받습니다.
이 프로그램에서 채널로 Resource 포인터를 본내는 것은 해당 데이터의 소유권을 본내는 쪽(sender)에서 받는 쪽(receiver)으로 이전한다는 규칙을 따릅니다. 이 규칙 덕분에 두 개 이상의 고루틴이 동시에 같은 Resource에 접근하지 않음을 알 수 있으며, 따라서 데이터 구조에 대한 잠금(locking)을 걱정할 필요가 없습니다.
3.3 Poller 함수
각 Poller는 입력 채널(in)으로부터 Resource 포인터를 받아 Poll() 메서드를 호출하고, 그 결과를 status 채널로 StateMonitor에 전달한 뒤, 완료된 Resource 포인터를 출력 채널(out)로 본냅니다. 이것은 "나 이 Resource 다 썼으니 main에 돌려줄게"라는 의미입니다.
3.4 채널 생성과 고루틴 시작
main에서는 pending과 complete라는 두 개의 *Resource 채널을 생성합니다. 그리고 StateMonitor를 초기화하고 여러 개의 Poller 고루틴을 시작합니다.
초기 작업을 시스템에 추가하기 위해, main은 별도의 고루틴에서 각 URL당 하나의 Resource를 pending 채널로 본냅니다. 이 별도 고루틴이 필요한 이유는 버퍼링되지 않은 채널의 전송과 수신이 동기적이기 때문입니다. 만약 main 고루틴에서 전송하고 Poller 수보다 URL 수가 많으면 데드락(deadlock)이 발생합니다 — 아직 complete에서 수신하지 않고 있기 때문입니다.
3.5 Sleep과 메인 이벤트 루프
Poller가 Resource를 다 사용하면 complete 채널로 본냅니다. main은 이 Resource들을 수신하여 Sleep() 메서드를 호출한 뒤 적절한 지연 후에 pending으로 다시 본냅니다. 각 수면은 별도의 고루틴에서 실행되어 병렬로 처리됩니다.
어떤 단일 Resource 포인터도 동시에 pending이나 complete 중 하나에만 있을 수 있습니다. 이로써 Resource는 Poller에 의해 처리되거나 수면 중이거나 둘 중 하나일 뿐, 동시에 두 상태가 될 수 없습니다. 이렇게 우리는 통신을 통해 Resource 데이터를 공유합니다.
3.6 StateMonitor
StateMonitor는 updates 채널로부터 State 값을 받아 urlStatus라는 맵에 최신 상태를 기록합니다. 그리고 ticker.C 채널로부터 주기적인 신호를 받아 현재 상태를 로그로 출력합니다.
이 고루틴이 urlStatus 데이터 구조를 소유하므로, 이 맵은 순차적으로만 접근됩니다. 이는 공유 맵에 대한 병렬 읽기/쓰기로 인해 발생할 수 있는 메모리 손상 문제를 방지합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4. 실전활용
• 웹 서비스 헬스 체크(health check) 시스템 구축
• 여러 워커(worker)가 작업 항목을 안전하게 공유하는 워커 풀 패턴
• 고루틴 간 데이터 소유권 이전을 통한 뮤텍스 없는(lock-free) 동시성 설계
• pipeline 패턴에서 각 단계(stage)가 채널을 통해 데이터를 주고받는 구조
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5. 정리
이 Codewalk는 Go의 동시성 프리미티브(primitives)를 사용하여 통신을 통해 메모리를 공유하는 간단한 예제를 탐구했습니다. 이것은 고루틴과 채널을 사용하여 표현력 있고 간결한 동시 프로그램을 작성하는 방법을 탐구하기 위한 출발점이 됩니다.
핵심 메시지: 메모리를 공유하여 통신하지 말고, 통신하여 메모리를 공유하라(share memory by communicating)!

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