더북(TheBook)

5.1 고루틴

고루틴(goroutine)은 Go 프로그램 안에서 동시에 독립적으로 실행되는 흐름의 단위로, 스레드와 비슷한 개념이다. 하지만 스레드와 달리 고루틴은 수 킬로바이트 정도의 아주 적은 리소스에서 동작하므로 한 프로세스에 수천, 수만 개의 고루틴을 동작시킬 수 있다. 고루틴은 정보를 공유하는 방식이 아니라 서로 메시지를 주고받는 방식으로 동작한다. 그래서 Lock으로 공유 메모리를 관리할 필요가 없고 구현도 어렵지 않다.

다음과 같이 go 키워드로 함수를 실행하면 새 고루틴이 만들어진다.

go f(x, y)

만들어진 고루틴 안에서는 f(x, y)가 실행된다.

다음 예제를 보자.


package main
 
import (
    "fmt"
    "time"
)
 
func main() {
    fmt.Println("main 함수 시작", time.Now())
    
    go long()
    go short()
     
    time.Sleep(5 * time.Second) // 5초 대기
    fmt.Println("main 함수 종료", time.Now())
}
 
func long() {
    fmt.Println("long 함수 시작", time.Now())
    time.Sleep(3 * time.Second) // 3초 대기
    fmt.Println("long 함수 종료", time.Now())
}
 
func short() {
    fmt.Println("short 함수 시작", time.Now())
    time.Sleep(1 * time.Second) // 1초 대기
    fmt.Println("short 함수 종료", time.Now())
}

메인 함수 안에서 longshort 함수는 go 키워드로 호출되고, 두 함수는 각각 새 고루틴을 생성하여 동시에 동작한다. 각 함수에서는 시작과 종료 시 메시지를 출력하게 하여 실행 시점과 종료 시점을 알 수 있게 했다.

다음은 함수의 실행 결과이다. 예상대로 각 함수가 동시에 실행된다.

실행 결과

main 함수 시작 2009-11-10 23:00:00 +0000 UTC
long 함수 시작 2009-11-10 23:00:00 +0000 UTC
short 함수 시작 2009-11-10 23:00:00 +0000 UTC
short 함수 종료 2009-11-10 23:00:01 +0000 UTC
long 함수 종료 2009-11-10 23:00:03 +0000 UTC
main 함수 종료 2009-11-10 23:00:05 +0000 UTC

고루틴을 사용할 때 주의할 점이 있다. 실행 중인 고루틴이 있어도 메인 함수가 종료되면 프로그램이 종료된다. 그래서 아직 실행 중인 고루틴이 있다면 메인 함수가 종료되지 않게 해야 한다. 이 예제에서 메인 함수의 대기 시간을 2초로 변경하고(time.Sleep(2 * time.Second)) 프로그램을 실행하면 long 함수가 종료되기 전에 프로그램이 종료되고 만다.

프로그램이 비정상적으로 종료되는 것을 피하려고 메인 함수에서 충분히 긴 시간 동안 대기하게 할 수도 있다. 하지만 각 고루틴이 얼마나 오랫동안 실행될지 알 수 없어서 이 방법은 효율적이지 않다. Go에서 제안하는 방법은 메인 함수에서 고루틴의 종료 상황을 확인할 수 있는 채널을 만들고, 만든 채널을 통해 종료 메시지를 대기시키는 것이다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.