golang管道


golang中的channel

channel用於goroutine之間的通信

如果不用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,不能再添加數據,但是可以讀取數據

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")
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM