...
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f("direct")
go f("goroutine")
go func(msg string) {
fmt.Println(msg)
}("going")
time.Sleep(time.Second)
fmt.Println("done")
}
go runtime
에서 go runtime scheduler
가 관리! 커널이 관리 xglobal queue, local queue
에 있는 실행 가능한 goroutine에 대한 포인터, 지금 실행하고 있는 고루틴에 대한 포인터, 스케쥴러의 cache와 reference를 담고 있다.channel
을 이용채널은 값을 전달하는 pipe라고 볼 수 있다.
채널은 전달하는 값의 타입에 따라 고정된다.
make(chan 타입) 으로 채널을 만들 수 있다.
채널 < - 값 형식으로 값을 넣을 수 있다.
변수 < - 채널 형식으로 넣은 값을 가져올 수 있다.
기본적으로 sender 와 receiver가 모두 준비 될 때 까지 send, receive가 블락되므로 별도의 동기화 구문 없이 다음과 같은 정보 전달이 가능하다.
...
func main() {
messages := make(chan string)
go func() { messages <- "ping" }()
msg := <-messages
fmt.Println(msg)
}
채널을 버퍼링 하도록 만들기 위해선 채널을 만들 때 다음과 같이 버퍼의 크기를 지정해주면 된다.
message := make(chan string, 2)
채널을 버퍼링하게 만들면 채널은 병행적으로 send와 receive를 처리하게 된다.
...
func main() {
messages := make(chan string, 2)
messages <- "buffered"
messages <- "channel"
fmt.Println(<-messages)
fmt.Println(<-messages)
}
만약 다음과 같이 receiver 구문을 다른 실행흐름에서 사용한다면 sender에서 값이 입력되기 전까지 실행 흐름이 block된다.
...
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done
}
채널을 함수의 인자로 사용할 때, 방향을 지정해주어 receive only 또는 send only로 지정할 수 있다.
이렇게 채널의 방향을 지정하는 것은 프로그램을 한층 더 type-safety하게 만들어 준다.
...
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
go pong(pings, pongs)
go ping(pings, "passed message")
fmt.Println(<-pongs)
}
여러개의 goroutine의 완료를 기다리기위해 select를 사용할 수 있다.
다음과 같이 case문을 통해 여러개의 채널 중 먼저 send가 끝난 채널을 수신하는 것이 가능하다.
...
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)
select 문을 통해 timeout을 쉽게 구현할 수 있다.
...
func main() {
c1 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(1 * time.Second):
fmt.Println("timeout 1")
}
위 코드에서 After는 인자로 받은 시간이 지나면 값을 할당받는다.
기본적으로 send와 receive는 block의 형태이지만 select의 default
구문을 사용해 non-blocking 으로 구현할 수 있다.
c1 := make(chan string)
select {
case res:= <- c1:
fmt.Println(res)
default:
fmt.Println("no data in c1")
} //non-blocking 형태의 receive
select {
case c1<-"msg":
fmt.Println("msg sent")
default :
fmt.Println("no receiver ready")
} //non-blocking 형태의 sender
close(chan)
함수를 이용하여 더 이상 보낼 값이 없는 채널의 입력을 닫을 수 있다.value, more <- my_channel
...
go func(){
value, more <- my_channel
if more{
fmt.Printf("data received %d",value)
} else{
fmt.Printf("no more data in channel")
return
}
}
...
close(my_channel)
for value:= range my_channel{
fmt.Println(value)
}
references:
https://calvinfeng.gitbook.io/gonotebook/concurrency/04-01-go-routines