Go의 sync 패키지는 고루틴 간 동시성 제어를 위해 사용됩니다. 주로 멀티 쓰레드 환경에서 데이터 경쟁을 방지하고 성능을 최적화하는 데 사용됩니다.
sync.Mutex
(뮤텍스 - Mutual Exclusion)sync.Mutex
는 한 번에 하나의 고루틴만 특정 코드 블록을 실행할 수 있도록 하는 상호 배제(뮤텍스, mutual exclusion) 락입니다.
package main
import (
"fmt"
"sync"
)
var (
counter int
mutex sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // 락 획득
counter++ // 공유 데이터 변경
mutex.Unlock() // 락 해제
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Final Counter:", counter) // 10이 보장됨
}
mutex.Lock()
→ 한 번에 하나의 고루틴만 counter++
실행 가능mutex.Unlock()
→ 다른 고루틴이 counter++
실행할 수 있도록 해제race condition
방지)sync.RWMutex
(읽기/쓰기 분리 락)sync.Mutex
와 유사하지만 읽기(Read) 연산을 동시에 여러 고루틴이 수행할 수 있도록 허용합니다.package main
import (
"fmt"
"sync"
"time"
)
var (
rwMutex sync.RWMutex
data int
)
func readData(id int, wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.RLock() // 읽기 락 (여러 고루틴이 동시에 실행 가능)
fmt.Printf("Reader %d: Read data = %d\\n", id, data)
time.Sleep(time.Millisecond * 100)
rwMutex.RUnlock()
}
func writeData(wg *sync.WaitGroup) {
defer wg.Done()
rwMutex.Lock() // 쓰기 락 (다른 모든 고루틴이 차단됨)
data++
fmt.Println("Writer: Updated data =", data)
time.Sleep(time.Millisecond * 200)
rwMutex.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go readData(i, &wg)
}
wg.Add(1)
go writeData(&wg)
wg.Wait()
}
RLock()
)이 동시에 실행 가능Lock()
)이 실행되면 모든 읽기/쓰기 연산이 차단sync.RWMutex
는 읽기 연산이 많고, 쓰기 연산이 적은 경우에 성능 최적화를 위해 사용합니다.sync.WaitGroup
(고루틴 완료 대기)sync.WaitGroup
은 고루틴이 종료될 때까지 기다리는 역할을 합니다.
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 작업 완료 시 WaitGroup 카운트 감소
fmt.Printf("Worker %d starting\\n", id)
time.Sleep(time.Second) // 작업 수행
fmt.Printf("Worker %d done\\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // WaitGroup 카운트 증가
go worker(i, &wg)
}
wg.Wait() // 모든 고루틴이 완료될 때까지 대기
fmt.Println("All workers done")
}