- Go는 goroutine이라는 일종의 경량 스레드를 제공한다.
- goroutine을 관리하는 Go Scheduler가 있다.
- Go Scheduler는 m:n 스케줄링 기법을 사용한다. 즉, m개의 goroutine을 n개의 OS 스레드로 멀티플렉싱(multiplexing)한다.
- Go Scheduler는 런타임 구성요소다.
- 병렬성은 “동시에 실행되는 것"을 의미한다.
- 동시성은 각 컴포넌트들이 최대한 독립적으로 실행될 수 있게 구성하는 것을 의미한다.
- 동시성을 지원하도록 소프트웨어를 작성해야 안전하게 병렬로 실행할 수 있다.
- goroutine은 백그라운드에서 실행된다.
func some_func() {
fmt.Println("Func!")
}
main() {
go some_func()
}
- goroutine은 특별히 신경쓰지 않으면 실행 순서를 제어할 수 없다. 즉, 각각의 goroutine이 순차적으로 실행됨을 보장하지 않는다.
func main() {
var waitGroup sync.WaitGroup
for i := range 10 {
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
fmt.Println("go")
}()
}
}
sync.WaitGroup
을 이용하면 모든 goroutine이 끝날 때까지 main함수가 종료되지 않게 할 수 있다.
waitGroup.Add()
와 waitGroup.Done()
은 동일한 호출횟수를 가져야 panic이나 fatal error가 발생하지 않는다.
waitGroup.Add()
가 waitGroup.Done()
보다 많이 호출되면 Done의 호출을 계속 기다리게 되는 데드락이 발생한다. (fatal error)
waitGroup.Done()
이 더 많이 호출되면 panic이 발생한다.
- 채널을 통해 goroutine끼리 데이터를 주고받을 수 있다.
- 각 채널마다 특정한 데이터 타입으로만 값을 교환할 수 있다.
func write(c chan int) {
c <- 10
close(c)
}
func main() {
c := make(chan int)
go write(c)
fmt.Println("Read: " <- c)
}
func f1(out <-chan int) {}
func f2(in chan<- int) {}
- 파이프라인은 goroutine과 채널을 연결하는 일종의 가상 메서드로 한쪽 goroutine의 출력을 다른쪽 goroutine의 입력으로 넘겨줄 수 있다.
func f1(out chan<- int) {
out <- 10
}
func f2(out chan<- int, in <-chan int) {
out <- in
}
func f3(in <-chan int) {
}
main() {
A := make(chan int)
B := make(chan int)
go f1(A)
go f2(B, A)
f3(B)
}
- 추가적으로 select, 시그널채널 등 보다 깊이 공부해야할 부분들이 있다.