golang之channel

1、channel簡介

go語言中併發程序非常簡單直接用關鍵字go就可以起一個協程,go語言的協程是語言級別的所以它的開銷比線程、進程要小,因爲線程和進程是系統級別的併發。go語言協程同步可以通過channel來解決。

channel有三種類型:

var aa chan int        /*可讀可寫*/
var aa chan <- int     /*只能寫*/
var aa <- chan int     /*只能讀*/

channel類型必須先用make初始化再使用

aa := make(chan int)

關閉channel用函數close,一個關閉的channel還可以去讀數據,但不能在寫如數據,如果寫入數據就會產生panic

aa := make(chan int)
close(aa)
aa <- 4    /*panic*/

 

 

2、channel阻塞

對於buffer爲1的channel,從channel中讀數據時當沒有讀到就會阻塞,向channel中寫數據如果沒有空間存儲也會阻塞。

讀阻塞:

aa := make(chan int)

b := <- aa    /*aa中沒有數據就會阻塞*/

寫阻塞:

aa := make(chan int)

aa <- 4
aa <- 5    /*4還沒有取出來,channel中沒有空間, 阻塞*/

3、channel非阻塞

當我們需要非阻塞的channel我們可以給channel分配空間,當空間沒有填滿channel不會阻塞,有buffer的channel相當於一個隊列,遵循先進先出的規則。

aa := make(chan int, 4)

帶buffer的channel讀取可以用range。但要注意的是當channel中的數據被取完後就會阻塞,如果沒有再往channel中寫數據就會產生死鎖

	w := make(chan int, 4)

	w <- 4
	w <- 5

	for b := range w {
        /*最後一個5取走後就會阻塞,產生死鎖*/
		fmt.Printf("w:%d\n", b)
	}

避免死鎖方法:

每次range讀取數據後判斷channel的長度,如果是0就說明channel爲空,就應該退出。

w := make(chan int, 4)

	w <- 1
	w <- 2
	w <- 3
	w <- 4

	for b := range w {
		fmt.Printf("w:%d\n", b)
        
        /*如果channel長度是0就退出循環,不然會阻塞產生死鎖*/
		if len(w) <= 0 {
			break
		}

	}

4、select

go中的select相當於c語言中的switch,是對對個channle做選擇,它不是循環,如果要循環判斷就要在外面在加一個for

package main

import (
	"fmt"
	"time"
)

func main() {
	a := make(chan int)
	b := make(chan int)
	defer close(a)
	defer close(b)

	go func() {
		for i := 1; i < 3; i++ {
			a <- i
			b <- (i + 1)
			time.Sleep(1 * time.Second)
		}

	}()

	for {
		select {
		case val1 := <-a:
			fmt.Println("val1:", val1)
		case val2 := <-b:
			fmt.Println("val2:", val2)
		case <-time.After(3 * time.Second): /*3秒超時*/
			fmt.Println("time out quit")
			return
		}
	}

}

select中可以加一個超時時間,超過這個時間通道中還沒有數據就直接返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章