Go可建立有buffer的channel。
使用make(chan T, bufferSize)
建立有buffer的channel如下。
c := make(chan string, 3) // create channel type string with buffer size 3
若bufferSize
參數省略或為0則建立的channel沒有buffer,也就是說channel值的收發要等到另一方可行才會動作。
例如下面在main goroutine中發收值到有buffer的channel。
package main
import "fmt"
func main() {
c := make(chan string, 3) // create channel with buffer size 3
c <- "hello" // send value to channel c, no block because of buffer
fmt.Println(<-c) // receive value from channel c and print
}
若改為無buffer的channel在發送值到channel時會發生deadlock,因為無buffer的channel發收要同時可行, 但下面在發送值到channel時先block,永遠無法執行下一行從channel接收值的敘述,所以發生deadlock。
package main
import "fmt"
func main() {
c := make(chan string) // create channel without buffer
c <- "hello" // deadlock! exit process
fmt.Println(<-c) // cannot be reached forever
}
要改用另一個goroutine發送值到channel,才可以正常收發。
package main
import "fmt"
func send(s string, c chan string) {
c <- s
}
func main() {
c := make(chan string)
go send("hello", c)
fmt.Println(<-c)
}
如果channel的buffer滿了一樣會阻塞。
package main
import "fmt"
func send(s string, c chan string) {
c <- s
}
func main() {
c := make(chan string, 2)
c <- "hello"
c <- "hi" // buffer is full
c <- "chao" // deadlock!
fmt.Println(<-c)
}
下面使用無buffer的channel,然後另外用三個goroutine發送值到channel,由於被block的不是main goroutine,所以沒有deadlock。也就是說任一個send goroutine的值會被main goroutine取出,而其餘兩個仍被block直到main goroutine結束。
package main
import "fmt"
func send(s string, c chan string) {
c <- s
}
func main() {
c := make(chan string)
go send("hello", c)
go send("hi", c)
go send("chao", c)
fmt.Println(<-c)
}
PingPong桌球範例(參考於Advanced Go Concurrency Patterns簡報)。桌子(table)是channel用來乘載球(ball)在不同玩家(player)的goroutine間的打擊次數資訊。
package main
import (
"fmt"
"time"
)
type Ball struct {
hits int
}
func Player(name string, table chan *Ball) {
for { // infinite loop
ball := <-table // receive ball from table
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond) // wait 100ms
table <- ball // send ball to table
}
}
func main() {
table := make(chan *Ball)
go Player("ping", table) // create player ping, pass ball throw table
go Player("pong", table) // create player pong, pass ball throw table
table <- new(Ball) // start play, send a ball to table
time.Sleep(1 * time.Second) // play 1 second
<-table // receive ball from table
}
認識到這邊覺得Go channel很類似Java的BlockingQueue
兩者都有buffer、阻塞、先進先出的特性。
沒有留言:
張貼留言