https://multifrontgarden.tistory.com/244

opengraph.png

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()의 짝이 맞도록 조심해야한다.