go channel 使用及機制流程彙總

使用channel

協程間通信的普通使用, 發送值給channel, 外部channel接收.bash

func t1() {
	ch := make(chan int)
	go func() {
		ch <- 1
	}()

	// <-ch // 致使下面語句阻塞
	fmt.Println("channel int", <-ch)
}
複製代碼
channel int 1
複製代碼

channel支持緩衝區ui

func t2() {
	ch := make(chan int, 3)
	go func() {
		ch <- 1
		ch <- 2
		ch <- 3
		ch <- 4 // 會阻塞寫不入, 除非ch已接收
		close(ch) // 關閉後不能再寫入, 而且緩衝區內爲空時則返回零值
	}()
	fmt.Println("buffer channel int",
		<-ch,
		<-ch,
		<-ch,
		<-ch,
		<-ch)
}
複製代碼
buffer channel int 1 2 3 4 0
複製代碼

select是專門給管道定製spa

func t3() {
	ch1 := make(chan int)
	ch2 := make(chan struct{})

	go func() {
		ch1 <- 2
	}()

	select {
	case <-ch1:
		fmt.Println("select here is ch1")
	case <-ch2:
		fmt.Println("select here is ch2")
	}
}
複製代碼
select here is ch1
複製代碼

使用for設計

func t4() {
	ch := make(chan int, 5)

	go func() {
		for i:=1; i<=5; i++ {
			time.Sleep(time.Millisecond * 10)
			ch <- i
		}
	}()

	for v := range ch {	// 會阻塞
		fmt.Println("for channel ", v)
		if v == 5 {
			break
		}
	}
}
複製代碼
for channel  1
for channel  2
for channel  3
for channel  4
for channel  5
複製代碼

同時使用selectfor3d

func t5() {
	chPrint := make(chan struct{}) 
	chStop := make(chan struct{}) 

	go func(){
		time.Sleep(time.Second * 1)
		chPrint <- struct{}{}

		time.Sleep(time.Second * 1)
		chPrint <- struct{}{}

		time.Sleep(time.Second * 1)
		chStop <- struct{}{}
	}()

	var sum int
	for {
		time.Sleep(time.Millisecond)

		select {
		case <-chPrint:
			fmt.Println("for+select now is", sum)
		case <-chStop:
			fmt.Println("for+select stop, result is", sum)
			return
		default:
			if sum == 10000 {
				fmt.Println("for+select end, result is", sum)
				return
			}
			sum += 1
		}
	}
}
複製代碼
for+select now is 766
for+select now is 1540
for+select stop, result is 2309
複製代碼

判斷管道是否關閉code

func t6() {
	ch := make(chan struct{})

	go func() {
		close(ch)
		//ch <- struct{}{} // 只運行這句, 輸出OK
	}()
	if _, ok := <-ch; ok {
		fmt.Println("if channel is ok")
		return
	}
	fmt.Println("if channel is bad")
}
複製代碼
if channel is bad
複製代碼

以只發送或只接收爲傳遞參數, 同理也能夠爲返回值cdn

func t7() {
	ch := make(chan struct{})
	chExit := make(chan struct{})

	go func(chRecv <-chan struct{}) {
		fmt.Println("recv channel")
		<-chRecv
		chExit<- struct{}{}
	}(ch)

	go func(chSend chan<- struct{}) {
		fmt.Println("send channel")
		chSend <- struct{}{}
	}(ch)

	<-chExit
}
複製代碼
send channel
recv channel
複製代碼

channel機制流程彙總

makechan()初始化hchan結構體, 若是沒有緩衝區即分配hchanSize大小的內存並返回;而有緩衝區的狀況下, 則計算管道元素類型大小並分配hchanSize+(elem.size * size)大小的內存(緩衝區是一個環形的結構設計), 最後返回hchan. 協程

chansend()channel發送數據, 首先鎖住當前協程, 有以下幾種狀況, 按順序判斷:blog

  1. 有正在等待的接收者, 就當即轉發給該接收者, 釋放鎖並退出.
  2. 有可用的緩衝區就將該值移到目標緩衝區等待被接收, 釋放鎖並退出.
  3. 是非阻塞(用於select)就退出, 釋放鎖並退出.
  4. 阻塞當前協程, 並將當前協程放到發送等待隊列中並釋放鎖, 喚醒後清理sudog.

chanrecv()接收channel的數據, 首先鎖住當前協程, 有以下幾種狀況, 按順序判斷:隊列

  1. channel關閉並無緩衝數據, 接收者接收的值將會是零值, 釋放鎖並退出.
  2. 發送隊列有發送者, 就當即接收該數據, 釋放鎖並退出.
  3. 有緩衝數據就將該數據複製給接收者, 釋放鎖並退出.
  4. 是非阻塞(用於select)就退出, 釋放鎖並退出.
  5. 阻塞當前協程, 並將當前協程放到發送等待隊列中並釋放鎖, 喚醒後清理sudog. 跟chansend差很少, 多了個已關閉並無緩衝數據的判斷.

closechan關閉channel, 首先也要獲取鎖, 關閉管道並釋放全部接收和發送的隊列並清醒全部sudog. 但緩衝區的數據不會清理, 隨時等待被接收.

相關文章
相關標籤/搜索