Go의 고루틴과 채널: 순차 프로세스 통신을 통한 실용적인 동시성
Go는 순차 프로세스 통신을 구현합니다: 초경량 고루틴, 채널을 파이프로 보안 통신을 위해 입력하고, 멀티플렉싱을 위해 선택하고, 동기화를 위해 WaitGroup을 선택하고 context.Context 전파된 삭제를 위한 컨텍스트 — 대부분의 경우 명시적인 뮤텍스가 없습니다. 경우의.
Go CSP 모델
가서 포옹해라 순차 프로세스 통신(CSP), 정식 모델 1978년 Tony Hoare가 제안했습니다. 기본 원칙: 여러 개의 고루틴을 사용하는 대신 동일한 변수(뮤텍스 사용)를 읽고 쓰는 경우, 고루틴은 다음을 통해 통신합니다. 채널 — 데이터 소유권을 전송하는 형식화된 파이프입니다.
Go 런타임은 OS 스레드 풀에서 고루틴 일정을 관리합니다(기본적으로
사용 가능한 코어 수에 따라 제어됨 GOMAXPROCS). 고루틴은
차단 작업(I/O, 채널 수신) 중에는 자동으로 중단되어 다른 작업을 허용합니다.
고루틴을 진행합니다.
고루틴: 구조 및 수명주기
package main
import (
"fmt"
"time"
)
func worker(id int, done chan struct{}) {
fmt.Printf("Worker %d started\n", id)
time.Sleep(100 * time.Millisecond) // simula lavoro
fmt.Printf("Worker %d finished\n", id)
done <- struct{}{} // segnala completamento
}
func main() {
done := make(chan struct{}, 5) // channel bufferizzato per 5
// Lancia 5 goroutine concorrentemente
for i := 0; i < 5; i++ {
go worker(i, done) // "go" avvia la goroutine
}
// Aspetta che tutte completino
for i := 0; i < 5; i++ {
<-done // riceve dal channel (blocca se vuoto)
}
fmt.Println("All workers done")
}
// Costo di una goroutine: ~2-8KB stack (cresce dinamicamente fino a 1GB)
// vs ~1MB per un OS thread
// Go può avere milioni di goroutine attive contemporaneamente
채널: 유형 및 패턴
버퍼링되지 않은 채널과 버퍼링된 채널
// Channel unbuffered: sincronizzazione diretta
// Send blocca finché un receiver è pronto
ch := make(chan int) // unbuffered
go func() { ch <- 42 }() // blocca finché qualcuno riceve
v := <-ch // sblocca il sender
// Channel buffered: coda FIFO con capacità fissata
// Send blocca SOLO se il buffer è pieno
buffered := make(chan int, 10) // buffer da 10 elementi
buffered <- 1 // non blocca (buffer ha spazio)
buffered <- 2 // non blocca
v := <-buffered // riceve 1 (FIFO)
// Directional channel types: sicurezza a compile time
func producer(ch chan<- int) { // solo write
ch <- 42
}
func consumer(ch <-chan int) { // solo read
v := <-ch
fmt.Println(v)
}
패턴: 파이프라인
// Pipeline: catena di goroutine connesse da channel
// Ogni fase legge dall'input channel e scrive sull'output channel
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out) // IMPORTANTE: chiudi quando finisci
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in { // range su channel riceve finché chiuso
out <- n * n
}
close(out)
}()
return out
}
func main() {
// Compone la pipeline
c := generate(2, 3, 4, 5)
out := square(c)
// Consuma l'output
for v := range out {
fmt.Println(v) // 4, 9, 16, 25
}
}
선택: 다중 채널에서의 다중화
select Go에서 경쟁을 위한 가장 강력한 키워드는 기다림입니다.
여러 채널을 동시에 실행하고 채널 준비 케이스를 실행합니다. 채널이 여러 개인 경우
준비가 되면 그는 무작위로 선택합니다.
// Timeout pattern con select
import "time"
func fetchWithTimeout(url string, timeout time.Duration) (string, error) {
resultCh := make(chan string, 1)
errCh := make(chan error, 1)
go func() {
result, err := fetch(url)
if err != nil {
errCh <- err
return
}
resultCh <- result
}()
select {
case result := <-resultCh:
return result, nil
case err := <-errCh:
return "", err
case <-time.After(timeout):
return "", fmt.Errorf("timeout after %v", timeout)
}
}
// Fan-out/fan-in con select e done channel
func merge(channels ...<-chan int) <-chan int {
merged := make(chan int)
var wg sync.WaitGroup
multiplex := func(ch <-chan int) {
defer wg.Done()
for v := range ch {
merged <- v
}
}
wg.Add(len(channels))
for _, ch := range channels {
go multiplex(ch)
}
go func() {
wg.Wait()
close(merged)
}()
return merged
}
context.Context: 전파된 삭제
패키지 context 삭제 전파를 위한 Go의 표준 메커니즘입니다.
고루틴 체인을 통해. I/O나 긴 작업을 수행하는 모든 함수는 이를 허용해야 합니다.
에 context.Context 첫 번째 매개변수로:
import (
"context"
"fmt"
"time"
)
// Funzione che rispetta la cancellazione
func longOperation(ctx context.Context, id int) error {
for i := 0; i < 10; i++ {
select {
case <-ctx.Done(): // controlla se il context è cancellato
return ctx.Err() // errore: context.Canceled o DeadlineExceeded
default:
fmt.Printf("Step %d/%d\n", i+1, 10)
time.Sleep(100 * time.Millisecond)
}
}
return nil
}
func main() {
// Context con timeout di 500ms
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel() // SEMPRE defer cancel() per liberare le risorse
err := longOperation(ctx, 1)
if err != nil {
fmt.Printf("Cancelled: %v\n", err) // context deadline exceeded
}
}
// Context si propaga attraverso le chiamate:
// Handler HTTP -> Service -> Repository -> Database
// Se il client disconnette, il context si cancella e risale tutta la catena
WaitGroup: 동기화 패턴
import "sync"
func processAll(items []Item) {
var wg sync.WaitGroup
results := make([]Result, len(items))
for i, item := range items {
wg.Add(1)
go func(i int, item Item) { // passa i e item come parametri!
defer wg.Done()
results[i] = process(item) // scrivere indici diversi è safe
}(i, item)
}
wg.Wait() // blocca finché tutti Done() sono stati chiamati
fmt.Println(results)
}
// ERRORE COMUNE: closure su variabile del loop (prima di Go 1.22)
// In Go 1.22+ il loop variable è scoped per iterazione -- no problema
for _, item := range items {
go func() {
process(item) // Go 1.22+: safe. Go <1.22: BUG! usa go func(i Item) invece
}()
}
인종 감지기: 인종 데이터 찾기
// Compila ed esegui con il race detector integrato
go run -race main.go
go test -race ./...
// Esempio di data race che il detector trova:
var counter int
func increment() {
counter++ // DATA RACE! lettura + scrittura non atomica
}
// go run -race stamperà:
// WARNING: DATA RACE
// Write at 0x... by goroutine 6:
// main.increment()
// Previous write at 0x... by goroutine 5:
// main.increment()
// Fix con atomic o mutex:
import "sync/atomic"
var atomicCounter int64
atomic.AddInt64(&atomicCounter, 1) // atomico, thread-safe
고루틴 누출: Go에서 가장 흔한 버그
수신 채널에서 멈추거나(아무도 전송하지 않음) 수신하지 못하는 고루틴
컨텍스트를 삭제하지 않는 것은 고루틴 누출입니다. 메모리와 CPU를 축적합니다. 항상 사용
context 긴 작업의 경우 각 채널에 발신자가 있는지 확인하세요.
및 수신기를 사용하거나 명시적인 정리와 함께 버퍼링된 채널을 사용합니다.
결론
Go의 동시성 모델은 백엔드 서비스에 가장 효과적인 모델 중 하나입니다.
초경량, 설계로 경쟁 조건을 방지하는 채널, select 다중화를 위해
전자 context 전파 취소의 경우. 대부분의 경쟁 Go 코드
명시적인 뮤텍스가 필요하지 않습니다.
다음 기사는 Python으로 넘어갑니다. asyncio.TaskGroup 그리고 구조화된 경쟁 Python 3.11+에서는 마침내 Go의 CSP에 필적하는 의미론적 안전성을 세상에 제공합니다. 비동기/대기.







