Search

[Go 기초] [#14] 고루틴과 채널

작성일시
2025/08/17 07:36
수정일시
2025/08/20 12:33
스택
Go
카테고리
Go
태그
goroutine
channel
1 more property

고루틴과 동시성

Go 언어의 가장 강력한 특징 중 하나는 동시성(Concurrency) 처리 능력입니다. Go는 경량 스레드인 '고루틴(Goroutine)'을 통해 간단하면서도 효율적인 동시성 프로그래밍을 지원합니다. 이번 강의에서는 고루틴의 개념부터 실제 사용법까지 알아보겠습니다.

고루틴이란?

고루틴은 Go 런타임에 의해 관리되는 경량 스레드입니다. 일반적인 스레드보다 훨씬 적은 메모리를 사용하며, 수천 개의 고루틴을 동시에 실행할 수 있습니다. 고루틴은 다른 고루틴과 독립적으로 실행되며, 이를 통해 효율적인 동시성 프로그래밍이 가능합니다.

고루틴 생성하기

Go에서 고루틴을 생성하는 방법은 매우 간단합니다. 함수 호출 앞에 go 키워드를 붙이기만 하면 됩니다.
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { // 고루틴으로 say 함수 실행 go say("world") // 메인 함수에서 직접 say 함수 호출 say("hello") } // 실행 결과 // hello // world // hello // world // hello // world // hello // world // hello
Go
복사
위 코드에서 go say("world")는 새로운 고루틴에서 say 함수를 실행합니다. 메인 함수는 고루틴이 시작되기를 기다리지 않고 바로 다음 줄로 진행하여 say("hello")를 실행합니다. 그 결과, "hello"와 "world"가 번갈아 출력됩니다.

채널(Channel)

고루틴 간에 데이터를 주고받기 위해서는 채널(Channel)을 사용합니다. 채널은 고루틴 간의 통신을 위한 파이프라인과 같은 역할을 합니다.
package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 채널 c로 sum 보내기 } func main() { s := []int{7, 2, 8, -9, 4, 0} // 정수형 채널 생성 c := make(chan int) // 두 개의 고루틴 실행 go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) // 채널에서 값 수신 x, y := <-c, <-c fmt.Println(x, y, x+y) } // 실행 결과 // -9 21 12
Go
복사
위 코드에서는 make(chan int)로 정수형 채널을 생성하고, 두 개의 고루틴에서 배열의 앞부분과 뒷부분의 합을 각각 계산한 후 채널로 전송합니다. 메인 함수에서는 채널로부터 두 결과를 받아 출력합니다.

버퍼 채널(Buffered Channel)

Go의 채널은 기본적으로 동기적입니다. 즉, 보내는 쪽과 받는 쪽이 모두 준비되어야만 데이터가 전송됩니다. 버퍼 채널을 사용하면 채널에 버퍼 공간을 할당하여 비동기적인 통신이 가능해집니다.
package main import "fmt" func main() { // 버퍼 크기가 2인 채널 생성 ch := make(chan int, 2) // 채널에 값 전송 ch <- 1 ch <- 2 // 채널에서 값 수신 fmt.Println(<-ch) fmt.Println(<-ch) } // 실행 결과 // 1 // 2
Go
복사
위 코드에서는 버퍼 크기가 2인 채널을 생성하여 두 개의 값을 연속적으로 보낸 후, 두 값을 차례로 받아옵니다. 버퍼가 가득 찰 때까지는 송신자가 블록되지 않습니다.

채널 닫기

작업이 완료되면 채널을 닫을 수 있습니다. 닫힌 채널에서는 더 이상 값을 보낼 수 없지만, 남아있는 값은 계속해서 받을 수 있습니다.
package main import "fmt" func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) // 채널 닫기 } func main() { c := make(chan int, 10) go fibonacci(10, c) // range로 채널에서 값 수신 for i := range c { fmt.Println(i) } } // 실행 결과 // 0 // 1 // 1 // 2 // 3 // 5 // 8 // 13 // 21 // 34
Go
복사
위 코드에서는 피보나치 수열을 계산하는 함수가 채널에 값을 보낸 후 채널을 닫습니다. 메인 함수에서는 range를 사용하여 채널이 닫힐 때까지 모든 값을 수신합니다.

select 문

select 문은 여러 채널 연산 중에서 준비된 것을 선택하여 실행할 수 있게 해줍니다. 이를 통해 여러 고루틴의 통신을 효율적으로 관리할 수 있습니다.
package main import ( "fmt" "time" ) func main() { c1 := make(chan string) c2 := make(chan string) // 첫 번째 고루틴 go func() { time.Sleep(1 * time.Second) c1 <- "one" }() // 두 번째 고루틴 go func() { time.Sleep(2 * time.Second) c2 <- "two" }() // 두 채널에서 메시지 수신 for i := 0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) } } } // 실행 결과 // received one // received two
Go
복사
위 코드에서는 두 개의 고루틴이 각각 다른 시간 간격으로 채널에 메시지를 보냅니다. select 문은 준비된 채널에서 먼저 값을 받아 처리합니다.

WaitGroup

여러 고루틴이 완료될 때까지 기다려야 할 때는 sync.WaitGroup을 사용할 수 있습니다.
package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // 함수가 종료되면 Done 호출 fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup // 5개의 고루틴 시작 for i := 1; i <= 5; i++ { wg.Add(1) // WaitGroup 카운터 증가 go worker(i, &wg) } // 모든 고루틴이 완료될 때까지 대기 wg.Wait() fmt.Println("All workers done") } // 실행 결과 // Worker 5 starting // Worker 3 starting // Worker 1 starting // Worker 4 starting // Worker 2 starting // Worker 2 done // Worker 5 done // Worker 1 done // Worker 4 done // Worker 3 done // All workers done
Go
복사
위 코드에서는 sync.WaitGroup을 사용하여 5개의 워커 고루틴이 모두 완료될 때까지 메인 함수가 종료되지 않도록 합니다.

끝마치며

고루틴은 Go 언어의 가장 강력한 기능 중 하나로, 복잡한 동시성 문제를 간단하게 해결할 수 있게 해줍니다. 채널을 통한 고루틴 간의 통신은 "통신을 통한 메모리 공유"라는 Go의 철학을 잘 보여줍니다.
축하합니다! Go 언어의 기초 과정을 모두 마쳤습니다. 변수, 제어 구문부터 고루틴과 채널까지 Go의 핵심 기능들을 배웠습니다. 이제 간단한 프로젝트를 직접 만들어보며 실력을 향상시켜 보세요.
Go 언어는 간결한 문법과 강력한 동시성 처리로 서버 개발, 클라우드 인프라 등 다양한 분야에서 활용됩니다. 이 강의가 여러분의 Go 학습에 도움이 되었길 바랍니다.
다음에 더 심화된 내용으로 찾아뵙겠습니다. 함께해 주셔서 감사합니다!
강의 목록

참조