天天看點

golang channel

什麼是channel

channels 是一種類型安全的消息隊列,充當兩個 goroutine 之間的管道,将通過它同步的進行任意資源的交換。chan 控制 goroutines 互動的能力進而建立了 Go 同步機制。當建立的 chan 沒有容量時,稱為無緩沖通道。反過來,使用容量建立的 chan 稱為緩沖通道。

要了解通過 chan 互動的 goroutine 的同步行為是什麼,我們需要知道通道的類型和狀态。根據我們使用的是無緩沖通道還是緩沖通道,場景會有所不同,是以讓我們單獨讨論每個場景。

無緩沖管道

make :

ch := make(chan struct{})

無緩沖 chan 沒有容量,是以進行任何交換前需要兩個 goroutine 同時準備好。當 goroutine 試圖将一個資源發送到一個無緩沖的通道并且沒有goroutine 等待接收該資源時,該通道将鎖住發送 goroutine 并使其等待。當 goroutine 嘗試從無緩沖通道接收,并且沒有 goroutine 等待發送資源時,該通道将鎖住接收 goroutine 并使其等待。

無緩沖信道的本質是保證同步。

golang channel

無緩沖channel在消息發送時需要接收者就緒。聲明無緩沖channel的方式是不指定緩沖大小。以下是一個列子:

package main

import (
	"sync"
	"time"
)

func main() {
	c := make(chan string)

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		c <- `foo`
	}()

	go func() {
		defer wg.Done()

		time.Sleep(time.Second * 1)
		println(`Message: `+ <-c)
	}()

	wg.Wait()
}
           

第一個協程會在發送消息

foo

時阻塞,原因是接收者還沒有就緒:這個特性在标準文檔中描述如下:

如果緩沖大小設定為0或者不設定,channel為無緩沖類型,通信成功的前提是發送者和接收者都處于就緒狀态。

effective Go文檔也有相應的描述:

無緩沖channel,發送者會阻塞直到接收者接收了發送的值。

為了更好的了解channel的特性,接下來我們分析channel的内部結構。

package main

import (
	"sync"
	"time"
	"fmt"
)


func main() {
	c := make(chan string)

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		c <- "foo"
	}()

	go func() {
		defer wg.Done()

		time.Sleep(time.Second)
		fmt.Println("message:" + <- c)
	}()

	wg.Wait()
}
/*
foo
*/
           

有緩沖管道

buffered channel 具有容量,是以其行為可能有點不同。當 goroutine 試圖将資源發送到緩沖通道,而該通道已滿時,該通道将鎖住 goroutine并使其等待緩沖區可用。如果通道中有空間,發送可以立即進行,goroutine 可以繼續。當goroutine 試圖從緩沖通道接收資料,而緩沖通道為空時,該通道将鎖住 goroutine 并使其等待資源被發送。

golang channel
package main

import (
	"sync"
	"time"
	"fmt"
)

func main() {
	c := make(chan string,2)

	var wg sync.WaitGroup
	wg.Add(2)

	go func(){
		defer wg.Done()

		c <- "foo"
		c <- "bar"
	}()

	go func(){
		defer wg.Done()

		time.Sleep(time.Second)
		fmt.Println("mesage:" + <- c)
		fmt.Println("message:" + <- c)
	}()

	wg.Wait()
}
/*
mesage:foo
message:bar
*/
           

追蹤耗時

通過Go工具trace中的

synchronization blocking profile

來檢視測試程式被同步原語阻塞所消耗的時間。接收時的耗時對比:無緩沖channel為9毫秒,緩沖大小為50的channel為1.9毫秒。

  • Send 先于 Receive 發生。
  • 好處: 延遲更小。
  • 代價: 不保證資料到達,越大的 buffer,越小的保障到達。buffer = 1 時,給你延遲一個消息的保障。

參考文章

  • https://www.it1352.com/807929.html
  • https://www.pengrl.com/p/21027/