- 큰 프로그램은 많은 작은 하위 프로그램으로 이뤄져있다. 예를 들어 웹 서버는 브라우져로부터 만들어진 요청을 다룬다. 그리고 HTML을 반환한다. 각각의 요청은 작은 프로그램처럼 다뤄진다. 하나 이상의 일들을 동시에 수행하기 위한 것이 동시성이다. 고는 동시성에 대해 고루틴과 채널로 풍부한 지원을 한다.
Goroutines
- 고루틴은 다른 함수들과 함께 동시성있게 동작할 수 있게 하는 함수이다. 고루틴을 만들기 위해 우리는 go 라는 키워드를 사용한다.
package main
import "fmt"
func f(n int) {
for i := 0; i < 10; i++ {
fmt.Println(n, ":", i)
}
}
func main() {
go f(0)
var input string
fmt.Scanln(&input)
}
- 이 프로그램은 두개의 고루틴이 있다. 첫번째는 main 자체에 숨어있다. 그리고 다음 고루틴은 go f(0)이다. 일반적으로 우리가 하나의 함수를 실행할 때, 프로그램은 함수의 모둔 statement를 실행한다. 그리고 다음 실행을 위해 다음 줄로 반환한다. 고루틴과 함께라면 우리는 함수가 끝나기를 기다리지 않고 바로 다음 줄로 반환한다. 이게 왜 프로그램이 모든 숫자를 출력하기 전에 Scanln의 호출이 포함되었는지에 대한 이유다.
- 고루틴은 가볍다. 그리고 수천개를 간단하게 만들 수 있다. 우리는 우리 프로그램을 10개의 고루틴으로 돌게끔 이렇게 수정할 수 있다.
func main() {
for i := 0; i < 10; i++ {
go f(i)
}
var input string
fmt.Scanln(&input)
}
- 이런 프로그램을 돌릴 때 동시에 돌아가기 보다는 고루틴 순서대로 돌아간다는 사실을 알아둬야 한다. 이제 딜레이를 추가해보자. 랜덤 값을 이용해서.
package main
import (
"fmt"
"time"
"math/rand"
)
func f(n int) {
for i := 0; i < 10; i++ {
fmt.Println(n, ":", i)
amt := time.Duration(rand.Intn(250))
time.Sleep(time.Millisecond * amt)
}
}
func main() {
for i := 0; i < 10; i++ {
go f(i)
}
var input string
fmt.Scanln(&input)
}
- f는 0부터 9까지의 수를 출력한다. 0에서 250 ms를 기다리고. 고루틴은 지금 동시에 동작하고 있는 것이다.
Channels
- 채널은 두개의 고루틴이 통신하는 방식을 제공한다. 그리고 그들의 실행을 동기화한다. 채널을 사용하는 예시를 보자.
package main
import (
"fmt"
"time"
)
func pinger(c chan string) {
for i := 0; ; i++ {
c <- "ping"
}
}
func printer(c chan string) {
for {
msg := <- c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string = make(chan string)
go pinger(c)
go printer(c)
var input string
fmt.Scanln(&input)
}
- 이 프로그램은 ping이라는 것을 계속 출력한다. 엔터를 치기 전까지.
- 채널 타입은 chan이라는 것으로 만들 수 있다. 그리고 채널을 통과할 타입을 적어줘야 한다.
- ← 는 채널에 보내는 메시지를 나타낸다. c ← "ping"은 "ping"이라는 것을 보낸다는 걸 의미한다.
- msg := ← c는 메시지를 받는 것을 의미한다. 그리고 msg에 저장한다는 것을 의미한다.
- fmt라인은 fmt.Println(← c)로 쓰여질 수 있다.