golang管道

golang中的channel

channel用於goroutine之間的通訊golang

若是不用channel,使用共享全局變量的方式,須要加鎖安全

// synchornized 同步
// golang中的 sync包中有互斥鎖 
var lock sync.Mutex  // mutex 互斥

lock.Lock() // 上鎖
// 多個goroutine同時對相同的數據進行修改
lock.Unlock() // 解鎖

使用同步鎖併發效率會很低數據結構

channel主要用於goroutine通訊和解決主線程等待goroutine執行結束再退出的問題併發

basic concept of channel

  1. 本質上是一個FIFO的數據結構-隊列
  2. 線程安全,不須要加鎖
  3. channel是有類型的,如字符串channel chan string,只能保存string數據

declare channel

var variableName chan dataType函數

var c1 chan int
var c2 chan bool
var c3 chan map[int]string

channel 是引用類型,必須初始化後才能使用線程

var c1 chan int
c1 = make(chan int, 4) // 使用make函數初始化chan int, 4是容量
// 初識化以後容量不可動態變化,channel不會自動擴容,寫入數據時不能超過容量,不然會deadlock
// fatal error: all goroutines are asleep - deadlock!

c1 <- 666  // 寫入數據

num := <- c1 // 取出數據, 每取一個數據,channel長度減一
// 不使用goroutine的狀況下   
// 當數據所有取出是再對channel取數據會報 deadlock

使用channel的注意事項

  1. 只保存指定類型數據
  2. 數據滿後,不能在添加數據,不然deadlock
  3. 從channel中取出數據,長度減一,騰出一個位置可添加數據
  4. 不使用goroutine的狀況下,當數據所有取出是再對channel取數據會報 deadlock

channel的遍歷和關閉

內置函數close(channel) 能夠關閉channel,不能再添加數據,可是能夠讀取數據code

channel能夠用for range方式遍歷協程

可是必須是已關閉的channel隊列

for range遍歷一個未關閉的channel會出現deadlock字符串

c1 := make(chan int, 10)
for i:=0; i<10; i++ {
    c1 <- i
}
close(c1)

for v := range c1 {
    fmt.Println(v)
}

對於一個已關閉的channel讀取數據時,若是數據已所有取出,取值狀態返回false而不會報錯

func main() {
    c1 := make(chan int, 2)
    c1 := make(chan int, 2)
    c1 <- 1
    c1 <- 2
    c2 <- 1
    c2 <- 2
    close(c1)
    <- c1  // 取出值 丟棄不用 1
    <- c1  // 2
    v, ok := <-c1  // 數據已取完
    fmt.Println(v,ok) // 0  false
    
    <- c2  // 1
    <- c2  // 2
    v, ok := <-c2  // 數據已取完 程序到這裏會出錯  deadlock
    fmt.Println(v,ok) 
}

對於已關閉的管道,能夠在取完數據時結束等待goroutine執行

for {
    _, ok := <- exitChan bool
    if !ok {
        break
    }
}

未關閉的channel須要取出指定個數的值以後結束等待goroutine執行

channel使用細節

channel默認是既能夠讀又能夠寫的

能夠聲明爲只讀或只寫

var c1 <- chan int // 只讀
var c2 chan <- int  // 只寫

通常channel不會聲明爲只讀和只寫,而是在聲明函數形參的時候使用

var c1 chan string

func sendData(c chan <- string) {...}

func readData(c <- chan string) {...}

// c1是默承認讀可寫的channel
// 當c1做爲參數傳給 sendData時 在 sendData 函數中只容許對管道進行寫操做
// 當c1做爲參數傳給 readData時 在 readData 函數中只容許對管道進行讀操做

goroutine中使用recover函數解決協程中出現的panic ,不影響主線程的執行

defer func() {
    if err = recover(); err != nil {
        ...dosomething
    }
}

select

select用於解決從管道中取數據的阻塞問題

不使用select,在遍歷未關閉的管道時會deadlock

然而,不少狀況下管道都是未關閉的,由於很差肯定何時關

用select來遍歷未關閉管道,不會deaklock

func main() {
    c1 := make(chan int, 10)
    c2 := make(chan string, 5)
    for i := 0; i < 10; i++ {
        rand.Seed(time.Now().UnixNano())
        c1 <- rand.Intn(100)
    }
    c2 <- "commerce"
    c2 <- "corresponding"
    c2 <- "oblige"
    c2 <- "decline"
    c2 <- "praise"

    label1:
    for {
        select {
        case v := <-c1:
            fmt.Println(v)
        case v := <-c2:
            fmt.Println(v)
        default:
            fmt.Println("兩個管道都沒數據了吧")
            break label1
        }
    }

    fmt.Println("adhere")
}
相關文章
相關標籤/搜索