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()) }
메인 함수 안에서 long과 short 함수는 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에서 제안하는 방법은 메인 함수에서 고루틴의 종료 상황을 확인할 수 있는 채널을 만들고, 만든 채널을 통해 종료 메시지를 대기시키는 것이다.