網頁

2021/7/6

Golang 使用Channel在goroutine間傳遞資料

Go語言使用Channelgoroutine併發程序中傳遞資料。


Channel是用來發送及接受指定型態值的管道,使用關鍵字chan宣告,後接傳遞資料的型態。例如下面宣告一個空的channel資料型態為string

var c chan string // declare c as channel with string type 
fmt.Println(c) // <nil>

通常使用make()函式建立channel。

c := make(chan string) // create channel c with string type

使用channel <- value把值放入channel,稱為send statements

c <- "hello" // send value "hello" to channel c

使用<-channel接收channel中的值,稱為receive operators

s := <-c // receive value from channel c then assign to variable s

channel值的收發預設會阻塞(block)直到另一動作可行。也就是說,如果goroutine A發送一個值給channel,但另一個goroutine B一直沒有從channel接收,則A的發送會被阻塞,直到B可接收後A的發送才會繼續。

下面範例在main程式中新增一個goroutine呼叫send()函式並發送值進channel,並在main的goroutine接收並印出,也就是說main goroutine和send goroutine使用channel來傳遞字串。

package main

import (
   "fmt"
)

func send(s string, c chan string) {
    c <- s // send s to channel c
}

func main() {
    c := make(chan string) // create channel c
    go send("hello", c)
    s := <-c       // receive value from channel c
    fmt.Println(s) // hello
}

而下面範例則在receive goroutine與send goroutine中以channel傳遞。main最後的time.Sleep()是為了防止main goroutine先結束,因為main goroutine先結束會直接離開整個程序而看不出receive goroutine與send goroutine的互動。

main.go

package main

import (
    "fmt"
    "time"
)

func send(s string, c chan string) {
    fmt.Println("send...")
    c <- s
}

func receive(c chan string) {
    fmt.Println("receive...")
    fmt.Println(<-c)
}

func main() {
    c := make(chan string)
    go receive(c)
    go send("hello", c)

    time.Sleep(time.Second * 1)
}

可能印出以下結果。

send...
receive...
hello

或印出以下,由此可看出channel的發收的阻塞效果。

receive...
send...
hello


而Channel可使用Go內建函式close()關閉。Channel關閉後則不可再送資料到Channel,否則會出現panic錯誤。

例如下面發送"hello"到Channel後以close()關閉。

main.go

package main

import (
    "fmt"
    "time"
)

func send(s string, c chan string) {
    fmt.Println("send...")
    c <- s
}

func receive(c chan string) {
    fmt.Println("receive...")
    fmt.Println(<-c)
}

func main() {
    c := make(chan string)
    go receive(c)
    go send("hello", c)

    time.Sleep(time.Second * 1)
    
    close(c)
    go send("world" c)
    time.Sleep(time.Second * 1)
}

上面Channel關閉後再發送"world"到Channel則發生panic: send on closed channel錯誤。


沒有留言:

張貼留言