網頁

2021/7/6

Golang Buffered Channel簡介

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、阻塞、先進先出的特性。


沒有留言:

張貼留言