일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 오블완
- Java
- 정보처리기사실기 기출문제
- mysql 튜닝
- Kubernetes
- kotlin querydsl
- minikube
- 기록으로 실력을 쌓자
- 티스토리챌린지
- CloudWatch
- 정보처리기사 실기 기출문제
- docker
- Spring
- APM
- kotlin coroutine
- go
- 코틀린 코루틴의 정석
- Elasticsearch
- Linux
- Pinpoint
- golang
- CKA
- 공부
- tucker의 go 언어 프로그래밍
- AI
- CKA 기출문제
- AWS EKS
- aws
- kotlin
- PETERICA
- Today
- Total
피터의 개발이야기
[GO] Tucker의 GO 언어 프로그래밍 - 22장 고루틴과 동시성 프로그래밍 본문

ㅁ 들어가며

ㅇ Tucker의 GO 언어 프로그래밍 책을 보고 정리한 글입니다.
ㅇ [GO] Tucker의 GO 언어 프로그래밍 - 목차
고루틴과 Go 언어의 동시성 프로그래밍에 대해 핵심 개념을 정리합니다.
ㅁ 스레드란?
프로세스 내에서 실행되는 실행 흐름의 최소 단위로, CPU 자원을 할당받아 작업을 수행합니다. OS 스레드는 컨텍스트 스위칭 비용이 높고 메모리를 많이 사용하지만, Go의 고루틴은 경량 스레드로 2KB의 작은 스택에서 시작해 동적으로 확장되며, Go 런타임이 직접 관리한다.
📌 고루틴 vs OS 스레드
OS 스레드: 컨텍스트 스위칭 비용高, 메모리 사용량多
고루틴: 빠른 생성/소멸, 수천 개 동시 실행 가능.
ㅁ 고루틴 사용
go
키워드로 함수를 비동기 실행한다. 메인 함수도 고루틴으로 실행되며, 메인 고루틴 종료 시 전체 프로그램이 종료된다.
func say(s string) {
fmt.Println(s)
}
func main() {
go say("World") // 새로운 고루틴 시작
say("Hello") // 메인 고루틴에서 실행
time.Sleep(time.Second)
}
Hello
World
ㅇ 출력 실행마다 순서가 다르다.
ㅁ 고루틴의 동작 방법
Go 런타임은 M:N 스케줄링으로 OS 스레드와 고루틴을 매핑합니다.
- 작업 훔치기(Work Stealing): 유휴 OS 스레드가 다른 스레드의 고루틴 큐에서 작업 가져옴
- 컨텍스트 스위칭: 고루틴 전환 비용이 매우 낮음(레지스터 3개만 저장).
- GMP 모델: Goroutine, Machine(OS 스레드), Processor 조합으로 효율적 병행성 관리.
ㅁ동시성 프로그래밍 주의점
var counter int
func increment() { counter++ }
func main() {
for i := 0; i < 1000; i++ {
go increment()
}
time.Sleep(time.Second)
fmt.Println(counter) // 매번 다른 값 출력 (예: 932)
}
- 경쟁 상태(Race Condition): 여러 고루틴이 동시에 공유 자원(counter)에 접근하면 예측 불가능한 결과 발생한다.
ㅁ 뮤텍스를 이용한 동시성 문제 해결
sync.Mutex
로 공유 자원에 대한 배타적 접근 보장.
var (
counter int
mu sync.Mutex
)
func increment() {
mu.Lock()
defer mu.Unlock()
counter++
}
// main 함수는 동일
ㅇ 출력: 1000
(항상 일관된 결과)
ㅇ 관련 글
- [GO] Go 언어에서의 "fatal error: concurrent map read and map write" 해결하기에서 동시성으로 인한 오류를 해결
ㅁ 뮤텍스와 데드락(p474)
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
var mu1, mu2 sync.Mutex
func a(index int) {
fmt.Printf("a, m1, %d\n", index)
mu1.Lock()
defer mu1.Unlock()
fmt.Printf("a, m2, %d\n", index)
mu2.Lock()
defer mu2.Unlock()
wg.Done()
}
func b(index int) {
fmt.Printf("b, m2, %d\n", index)
mu2.Lock() // mu1 대신 mu2 먼저 잠금
defer mu2.Unlock()
fmt.Printf("b, m1, %d\n", index)
mu1.Lock()
defer mu1.Unlock()
wg.Done()
}
func main() {
wg.Add(2)
for i := 0; i < 5; i++ {
go a(i)
go b(i)
}
wg.Wait()
}
# 반복 실행하면 다르게 출력된다.
b, m2, 3
b, m2, 2
a, m1, 2
a, m2, 2
b, m1, 3
b, m2, 0
a, m1, 1
a, m1, 3
a, m1, 4
b, m2, 4
a, m1, 0
b, m2, 1
fatal error: all goroutines are asleep - deadlock!
ㅇ 잠금 순서가 불일치할 경우 발생하는 교착 상태 예시
ㅇ 뮤덱스의 Lock() 메서드를 호출해 다른 뮤텍스가 반납될 때까지 대기하게 된다.
ㅇ 여러 고루틴이 통시에 락을 걸면 데드락이 발생한다.
ㅇ 멀티코더에서 여러 고루틴은 동시적 프로그램으로 성능을 향상 시킬 수 있지만 데드락으로 인해 심각한 문제가 발생 할 수 있다.
ㅁ 또 다른 자원 관리 기법
- 채널(Channel): 고루틴 간 안전한 데이터 전송
ch := make(chan int) go func() { ch <- 42 }() fmt.Println(<-ch) // 42
- sync.WaitGroup: 고루틴 그룹의 완료 대기[2]
- Atomic 연산:
sync/atomic
패키지로 원자적 연산 수행
ㅁ 마무리
고루틴은 Go의 동시성 프로그래밍 핵심 도구로,
- go
키워드로 간편한 비동기 실행
- 뮤텍스/채널로 안전한 자원 공유
- 데드락 주의와 적절한 동기화 기법 필수
동시성 설계 시 채널을 우선 사용하고, 공유 메모리가 필요할 때 뮤텍스를 적용하는 것이 중요한 포인트이다.
ㅁ 함께 보면 좋은 사이트
'Programming > GO' 카테고리의 다른 글
[GO] Tucker의 GO 언어 프로그래밍 - 24장 제네릭 프로그래밍 (0) | 2025.03.27 |
---|---|
[GO] Tucker의 GO 언어 프로그래밍 - 23장 채널과 컨텍스트 (1) | 2025.03.26 |
[GO] Tucker의 GO 언어 프로그래밍 - 4장 연산자 (0) | 2025.03.23 |
[GO] Tucker의 GO 언어 프로그래밍 - 3장 fmt (0) | 2025.03.21 |
[GO] Tucker의 GO 언어 프로그래밍 - 2장 변수 (0) | 2025.03.08 |