go快速學(xué)習(xí)_Channel 管道

Channel的基本概念

Channal就是用來通信的,像Unix下的管道一樣,
它的操作符是箭頭" <-" , 箭頭的指向就是數(shù)據(jù)的流向

ch <- v // 發(fā)送值v到Channel ch中
v := <-ch // 從Channel ch中接收數(shù)據(jù),并將數(shù)據(jù)賦值給v

下面的程序演示了一個(gè)goroutine和主程序通信的例程。

package main

import "fmt"

func main() {
    //創(chuàng)建一個(gè)string類型的channel
    channel := make(chan string)

    //創(chuàng)建一個(gè)goroutine向channel里發(fā)一個(gè)字符串
    go func() { channel <- "hello" }()

    msg := <- channel
    fmt.Println(msg)
}

chan為先入先出的隊(duì)列,有三種類型,雙向,只讀,只寫,分別為"chan","chan<-","<-chan"
初始化時(shí)候,可以指定容量make(chanint,100);容量(capacity)代表Channel容納的最多的元素的數(shù)量

Channel的阻塞

channel默認(rèn)上是阻塞的,也就是說,如果Channel滿了,就阻塞寫,如果Channel空了,就阻塞讀。于是,我們就可以使用這種特性來同步我們的發(fā)送和接收端。

package main

import "fmt"
import "time"

func main() {

    channel := make(chan string) //注意: buffer為1

    go func() {
        channel <- "hello"
        fmt.Println("write \"hello\" done!")

        channel <- "World" //Reader在Sleep,這里在阻塞
        fmt.Println("write \"World\" done!")

        fmt.Println("Write go sleep...")
        time.Sleep(3*time.Second)
        channel <- "channel"
        fmt.Println("write \"channel\" done!")
    }()

    time.Sleep(2*time.Second)
    fmt.Println("Reader Wake up...")

    msg := <-channel
    fmt.Println("Reader: ", msg)

    msg = <-channel
    fmt.Println("Reader: ", msg)

    msg = <-channel //Writer在Sleep,這里在阻塞
    fmt.Println("Reader: ", msg)
}

結(jié)果為

Reader Wake up...
Reader:  hello
write "hello" done!
write "World" done!
Write go sleep...
Reader:  World
write "channel" done!
Reader:  channel

總結(jié)

  • channel的定義
    • 定義語法 :
      • var ch =make(chan 通道中傳遞的數(shù)據(jù)類型,容量大小) 0為無緩沖,其余為有緩沖
    • 讀channel:
      • <-ch 讀到數(shù)據(jù)拋空,用來調(diào)節(jié)各個(gè)go程的先后順序
      • num := <-ch 讀到數(shù)據(jù),存入num中
    • 寫channel:
      • ch <- data data類型嚴(yán)格于定義的語法一致
    • 特性:
      1. 通道中的數(shù)據(jù)只能單向流動(dòng)。一端讀端,另一端必須寫端。
      2. 通道中的數(shù)據(jù)只能一次讀取,不能重復(fù)讀。
      3. 讀端和寫端在不同的go程之間
      4. 讀端讀,寫端不在寫,讀端阻塞。寫端寫,讀端不在線,寫端阻塞。
    • 系統(tǒng)3個(gè)特殊文件:
      • stdin:標(biāo)準(zhǔn)輸入文件 ---鍵盤
      • stdout:標(biāo)準(zhǔn)輸出文件 ---屏幕
      • stderr:標(biāo)準(zhǔn)錯(cuò)誤文件 ---屏幕
  • channel分類
    • 無緩沖
      • 要求 讀端寫端同時(shí)在線,對(duì)端不在線,本端阻塞
    • 有緩沖
      • 讀端,不在線,寫端可以將數(shù)據(jù)直接寫入緩沖區(qū)(不阻塞) 直到緩沖區(qū)被寫滿,依然沒有讀端,寫端阻塞

go程間通信

  • 多個(gè)go程間,如果有多個(gè)共享資源時(shí),需要分別同步

同步通信,異步通信

  • 同步通信:——無緩沖channel
    • 一個(gè)調(diào)用發(fā)出,如果沒有得到結(jié)果,那么該調(diào)用不返回 ——阻塞
  • 異步通信:
    • 一個(gè)調(diào)用發(fā)出,不等待結(jié)果,直接返回——不阻塞

關(guān)閉channel

  • 當(dāng)寫端,寫完數(shù)據(jù)后,使用close(ch)關(guān)閉channel

  • 讀端,可以判斷channel是否可讀

  • for{
      if num,ok:= <-ch;ok{
        fmt.Println("num=",num)//讀ch中的數(shù)據(jù)
      }else{
        break  //跳出數(shù)據(jù)
      }
    }
    // 讀channel的內(nèi)容,結(jié)束后自動(dòng)跳出
    // 是上面一種的簡(jiǎn)單寫法
    for num := range ch{
    }
    
  • 關(guān)閉channel的特性:

    • 如果寫端沒有關(guān)閉,暫停寫入,讀端阻塞等待
    • 如果寫端已經(jīng)關(guān)閉,不能寫入,報(bào)錯(cuò):==panic:send on closed channel==
    • 如果寫端已經(jīng)關(guān)閉,讀端依然能讀取,讀到的是數(shù)據(jù)類型的零值

單向channel

  • 定義語法:
    • 雙向channel:
      • var ch chan 數(shù)據(jù)類型
    • 單向讀channel:
      • var chr <- chan int
      • chr = ch 雙向channel給單向讀channel賦值
      • 只能做讀操作不能做寫操作
    • 單向?qū)慶hannel:
      • var chw chan <- int
      • chw = ch 雙向channel給單向?qū)慶hannel賦值
      • 只能做寫操作不能做讀操作
  • 單向channel==不能==給雙向channel賦值
  • 單向channel的使用:
    • 主要應(yīng)用于函數(shù)傳參,單向channel可以在語法層面限定channel使用的方法
      • 單向讀:不能寫
      • 單向?qū)懀翰荒茏x

Select關(guān)鍵字

多個(gè)Channel的select

package main
import "time"
import "fmt"

func main() {
    //創(chuàng)建兩個(gè)channel - c1 c2
    c1 := make(chan string)
    c2 := make(chan string)

    //創(chuàng)建兩個(gè)goruntine來分別向這兩個(gè)channel發(fā)送數(shù)據(jù)
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "Hello"
    }()
    go func() {
        time.Sleep(time.Second * 1)
        c2 <- "World"
    }()

    //使用select來偵聽兩個(gè)channel
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

select的使用

  • 作用:用來監(jiān)聽channel上的數(shù)據(jù)流動(dòng)
  • 特性:
    • 每一個(gè)case分支,都必須是IO操作
    • 通常將select置于for循環(huán)中
    • 一個(gè)case監(jiān)聽的channel不滿足監(jiān)聽條件,當(dāng)前case分支阻塞
    • 當(dāng)所有case分支都不滿足監(jiān)聽條件時(shí),select如果有default分支時(shí),走default,否則等待case滿足
    • 當(dāng)監(jiān)聽的多個(gè)case分支中,同時(shí)滿足多個(gè)case,隨機(jī)選擇任意一個(gè)執(zhí)行
    • 為防止忙輪詢出現(xiàn),可以適當(dāng)選擇省略default
  • break關(guān)鍵字不能應(yīng)用于結(jié)束整個(gè)select,只能跳出一個(gè)分支
  • ==結(jié)論==:所有使用select的go程,與其他go程間,通信時(shí),采用的是==異步通信==
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容