https://multifrontgarden.tistory.com/244
golang의 장점중 하나는 병렬 프로그래밍이 매우 쉽다는 것이다. 활용법을 알아보자.
func main() {
go goroutine()
}
func goroutine() {
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
goroutine으로 실행할 함수를 정의하고(익명함수로 해도 된다.) 앞에 go
키워드만 적어주면 끝이다. 하지만 이 코드를 실행하면 아무것도 출력되지않는걸 볼 수있다. main 함수가 goroutine을 생성하고 바로 끝나버리기때문에 생성된 goroutine이 바로 종료되기때문이다.
func main() {
go goroutine()
time.Sleep(time.Second * 1)
}
func goroutine() {
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
main 함수가 병렬로 실행되는 goroutine을 기다릴수있도록 sleep() 함수를 호출하게했다. 이게 정상적으로 출력된다. 하지만 golang에서는 sleep() 보다 좀 더 우아한 방식을 지원하고있다.
func main() {
var wg sync.WaitGroup
wg.Add(1)
go goroutine(&wg)
wg.Wait()
}
func goroutine(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
sync 패키지의 WaitGroup 구조체를 활용해서 다른 goroutine을 기다리게 작성할 수 있다. Add() 함수는 기다릴 goroutine의 갯수를 증가시키는 함수이고, Done() 함수는 그 갯수를 줄이는 함수다. 고로 goroutine을 생성하기전에는 Add()를 호출해야하고, goroutine을 이용해서 실행되는 함수에서는 꼭 완료 후 Done()을 실행시키도록 해야한다.
Add()와 Done()의 짝이 안맞으면 에러가 발생하게된다.
func main() {
var wg sync.WaitGroup
wg.Add(2)
go goroutine(&wg)
wg.Wait()
}
func goroutine(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
위 코드는 fatal error: all goroutines are asleep - deadlock!
이란 에러메세지를 내뿜게된다. 2개가 Add() 됐는데 Done()은 1번만 호출되니 메인 goroutine이 계속 기다리다가 데드락에 걸리는 것이다.
func main() {
var wg sync.WaitGroup
wg.Add(0)
go goroutine(&wg)
wg.Wait()
}
func goroutine(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
이 코드는 처음 예제와 마찬가지로 아무것도 출력하지않는다. Add() 된게 없으니 함수가 그냥 종료되어버리는것이다.
func main() {
var wg sync.WaitGroup
wg.Add(0)
go goroutine(&wg)
time.Sleep(time.Second * 1)
wg.Wait()
}
func goroutine(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
fmt.Print(i)
}
}
억지로 기다리게하면 panic: sync: negative WaitGroup counter
이런 에러메세지와 함께 종료된다.
지금은 간단한 예제이지만 goroutine을 활용하게되면 반복문 내에서 goroutine을 생성하는 경우도 많다. 이럴때 Add()와 Done()의 짝이 맞도록 조심해야한다.