Goroutine은 Go 언어에서 지원하는 경량(lightweight)의 독립적인 실행 단위로, 병행성(concurrency)을 쉽게 구현할 수 있도록 설계된 메커니즘이다. 전통적인 스레드(thread)와 비교할 때, 적은 메모리 오버헤드와 높은 성능을 제공한다.
Goroutine의 주요한 특징은 다음과 같다.
M은 OS 스레드 개수, N은 Goroutine 개수를 의미한다.N)을 적은 수의 OS 스레드(M)에 매핑해 실행한다.Goroutine은 간단히 함수 호출 앞에 go 키워드를 붙여 실행한다.
// 일반적인 함수 호출
myFunc()
// Goroutine을 통한 비동기 호출
go myFunc()
예제는 다음과 같다.
package main
import (
"fmt"
"time"
)
func printMessage(msg string) {
for i := 0; i < 3; i++ {
fmt.Println(msg, ":", i)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
go printMessage("goroutine") // Goroutine 생성
printMessage("main") // 메인 함수 실행
}
위 코드 실행 결과는 다음과 같다.
main : 0
goroutine : 0
main : 1
goroutine : 1
main : 2
goroutine : 2
위와 같이 Goroutine이 동시에 병행하여 실행된다.
병행 실행 중 Goroutine 간 데이터 경쟁(Race Condition)을 방지하기 위해 동기화 메커니즘이 필요하다. 주로 다음 두 가지 방법이 사용된다.
채널은 Goroutine 간 통신과 동기화를 지원하는 자료구조이다.
package main
import "fmt"
func worker(done chan bool) {
fmt.Println("Working...")
done <- true // 작업 완료 알림
}
func main() {
done := make(chan bool)
go worker(done)
<-done // 작업 완료 대기
fmt.Println("Done")
}
뮤텍스는 공유된 리소스에 접근할 때 상호 배제를 보장한다.
package main
import (
"fmt"
"sync"
)
var counter int
var mutex sync.Mutex
func increment() {
mutex.Lock()
counter++
mutex.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
Goroutine은 Go 언어에서 병행성 프로그래밍을 효율적이고 간단하게 구현할 수 있도록 설계된 핵심 요소로, 낮은 비용으로 높은 성능을 발휘한다. 그러나 사용 시 데이터 경쟁 및 동기화 문제에 주의해야 하며, 이를 적절히 관리할 수 있는 기법을 함께 사용하는 것이 권장된다.