Go 學習筆記08.協程和管道

go 協程

package main

import "fmt"

func f(from string) {
    for i := 0; i < 3; i++ {
        fmt.Println(from, ":", i)
    }
}

func main() {

    // 正常調用方法
    f("normal")

    // 協程
    // go f()
    go f("go")

    // go 匿名函數(going)
    go func(msg string) {
        fmt.Println(msg)
    }("going")

    // 等待輸入,我們阻塞一下
    // 如果不阻塞,協程來不及執行,主程序就會退出了
    var input string
    fmt.Scanln(&input)
    fmt.Println("done")
}

管道

管道用來連接多個協程,收發數據

package main

import "fmt"

func main() {

    // 創建一個管道
    // 管道中傳輸的數據是string, 管道使用chan關鍵字
    messages := make(chan string)

    // 看箭頭的方向, 下面的是向管道內輸入數據
    go func() { messages <- "ping" }()

    // 從管道中接收數據
    msg := <-messages
    fmt.Println(msg)
}

// 默認發送和接收操作是阻塞的,直到發送方和接收方都準備完畢。

緩衝管道

緩衝管道與普通無緩衝管道的區別

簡單說 緩衝管道,可以設定管道可存信息的大小

無緩衝普通的,大小就是1,有一個消息不取出就無法再次放入

ps: 測試是無緩衝管道,連續放入兩個編譯或執行的時候會報錯

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:

https://blog.csdn.net/sgsgy5/article/details/82054902

package main

import "fmt"

func main() {

    // 設置緩衝管道大小2
    messages := make(chan string, 2)

    // 發送
    messages <- "buffered"
    messages <- "channel"

    // 接收
    fmt.Println(<-messages)
    fmt.Println(<-messages)
}

管道同步

package main

import "fmt"
import "time"


func worker(done chan bool) {
    fmt.Print("working...\n") // fmt.Print 不會換行,而Println會換行
    time.Sleep(time.Second)
    fmt.Println("done")
    // 發送一個值來通知我們已經完工啦。
    done <- true
}

func main() {

    // 創建管道,管道容量1 接收參數布爾類型
    // 用這個管道阻塞住主進程,這樣就可以其到協程管道同步的效果了
    done := make(chan bool, 1)
    go worker(done)

    // 阻塞住,直到worker協程執行完畢後向管道中發送消息
    <-done
}

管道方向

select case

管道chan作爲參數 傳入到函數時

可以規定這個管道 只能發 或 只能收,當然還可以不做限制

package main

import "fmt"

// pings是管道,通過箭頭的方向,我們可以猜到 這個限制的是 只能向管道里傳入值
// 也就是隻能發chan-< 
// 如果這樣 <-chan 就行只能收
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)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

管道選擇器

select…case

管道選擇器可以讓我們同時等待多個管道操作

通常 go協程+管道+管道選擇器 結合是Go一個強大的特性

感覺和python的異步協程中的loop差不多

package main

import "time"
import "fmt"

func main() {

    // 創建2個管道
    c1 := make(chan string)
    c2 := make(chan string)

    // go協程 執行匿名函數,休眠1秒,傳入one
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    // 休眠2秒,傳入two
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    // 使用關鍵字 select..case 
    // 哪個先完成,就先執行哪個case,然後向下執行
    // 所以我們這裏循環了2次,目的是讓他們都得到執行,最後耗時應該是2秒多一點點,不會是3秒
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章