1. 程式人生 > >Golang 之 協程 goroutine(三)

Golang 之 協程 goroutine(三)

“通道” 是連線多個 Go 協程的管道。 可以從一個 Go 協程, 將值傳送到通道,然後在別的 Go 協程中接收。

package main

import "fmt"

func main() {

    // 使用 `make(chan val-type)` 建立一個新的通道。
    // 通道型別就 val-type 是他們需要傳遞值的型別。
    // 此處建立名為 message 的通道
    messages := make(chan string)

    // 使用 `channel <-` 語法 _傳送_ 一個值到通道中。這裡
    // 我們在一個新的 Go 協程中傳送 `"hello channel"` 到上面建立的`messages` 通道中。
go func() { messages <- "hello channel" }() // 使用 `<-channel` 語法從通道中 _接收_ 一個值。這裡 // 將接收我們在上面傳送的 `"hello channel"` 訊息並打印出來。 // 傳送和接收操作是阻塞的,直到傳送方和接收方都準備完畢。 // 這個特性允許,不使用任何其它的同步操作,來在程式結尾等待 // 訊息 `"hello channel"` msg := <-messages fmt.Println(msg) }

死鎖問題: messages <- “hello channel” 與 msg := <-messages 必須同時進行,不然會出現死鎖

package main

import "fmt"

func main() {


    messages := make(chan string)
    // 此時已經阻塞在 messages <- "hello channel"
    // 解決方案 1. 在messages <- "hello channel"之前,加協程去消費該通道,或者在消費通道之前加協程往通道傳送
    // 解決方案 2. 給messages通道加緩衝防止阻塞
    messages <- "hello channel"
    msg := <-messages
    fmt.Println(msg)
}
// 執行結果
//fatal error: all goroutines are asleep - deadlock! //goroutine 1 [chan send]: //main.main()

在準備傳送往通道傳送資料之前,開啟協程去消費通道

package main

import (
    "fmt"
    "time"
)

func f1(msg chan string) {
    fmt.Println(<-msg)
}

func main() {


    messages := make(chan string)
    // 此時已經阻塞在 messages <- "hello channel"
    // 解決方案 1. 在messages <- "hello channel"之前,加協程去消費該通道
    go f1(messages)
    messages <- "hello channel"

    // 防止協程在執行途中,因主執行緒的結束而終止
    time.Sleep(time.Second * 3)
}

在消費通道之前加協程往通道傳送 messages <- “hello channel”

package main

import (
    "fmt"
    "time"
)


func main() {


    messages := make(chan string)
    // 此時已經阻塞在 messages <- "hello channel"
    // 解決方案 1. 在消費通道之前加協程往通道傳送 messages <- "hello channel"
    go func() {messages <- "hello channel"}()
    fmt.Println( <-messages )

    // 防止協程在執行途中,因主執行緒的結束而終止
    time.Sleep(time.Second * 3)
}

通道緩衝 ,給messages通道加緩衝防止阻塞,通道是 無緩衝 的,這意味著只有在對應的接收(<- chan),通道準備好接收時,才允許進行傳送(chan <-)。可快取通道,允許在沒有對應接收方的情況下,快取限定數量的值。

package main

import (
    "fmt"
    "time"
)


func main() {

    // 最多允許快取一個,此時多傳送和多接收,都會引起死鎖,所以要保證傳送一個,列印一個
    messages := make(chan string, 1)
    // 此時已經阻塞在 messages <- "hello channel"
    // 解決方案 2. 給messages通道加緩衝防止阻塞
    messages <- "hello channel"
    msg := <-messages
    fmt.Println(msg)

    // 防止協程在執行途中,因主執行緒的結束而終止
    time.Sleep(time.Second * 3)
}

通道同步: 使用通道來同步 Go 協程間的執行狀態。這裡是一個使用阻塞的接受方式來等待一個 Go 協程的執行結束。

package main

import "fmt"
import "time"

// 這是將要在 Go 協程中執行的函式。
// `done` 通道將被用於通知其他 Go 協程這個函式已經工作完畢。
func worker(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")

    // 傳送一個值來通知已經完工。
    done <- true
}

func main() {

    // 執行一個 worker Go協程,並給予用於通知的通道。
    done := make(chan bool, 1)
    go worker(done)

    // 程式將在接收到通道中 worker 發出的通知前一直阻塞。
    fmt.Println("阻塞中")
    <-done
}