...
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