golang:Channel協程間通信


      channel是Go語言中的一個核心數據類型,channel是一個數據類型,主要用來解決協程的同步問題以及協程之間數據共享(數據傳遞)的問題。在並發核心單元通過它就可以發送或者接收數據進行通訊,這在一定程度上又進一步降低了編程的難度。

      goroutine運行在相同的內存地址空間,channel可以避開所有內存共享導致的坑;通道的通信方式保證了同步性。數據通過channel:同一時間只有一個協程可以訪問數據:所以不會出現數據競爭,確保並發安全。

channel的定義

channel是對應make創建的底層數據結構的引用。 創建語法: make(chan Type, capacity)

channel := make(chan bool) //創建一個無緩沖的bool型Channel
,等價於make(chan Type, 0)
channel := make(chan bool, 1024) //創建一個有緩沖,切緩沖區為1024的bool型Channel


channel <- x           //向一個Channel發送一個值
<- channel             //從一個Channel中接收一個值
x = <- channel         //從Channel c接收一個值並將其存儲到x中
x, ok = <- channel     //從Channel接收一個值,如果channel關閉了或沒有數據,那么ok將被置為false

      channel是一個引用類型,當復制一個channel或用於函數參數傳遞時,我們只是拷貝了一個channel引用,因此調用者和被調用者將引用同一個channel對象。和其它的引用類型一樣,channel的零值(定義未初始化)也是nil。

      在默認情況下,channel接收發送數據都是阻塞的,(channel <- 1,寫端寫數據,讀端不在讀。寫端阻塞; str := <-channel 讀端讀數據, 同時寫端不在寫,讀端阻塞。)除非另一端已經准備好,這樣就使得goroutine同步變的更加的簡單,而不需要顯式的lock。

示例

package main

import (
	"fmt"
	"runtime"
	"time"
)

var c = make(chan int32)

func printstr(s string) {
	for _, value := range s {
		fmt.Printf("寫入%+q\r\n", value)
		time.Sleep(time.Second)
		c <- value
	}
}

func main() {
	runtime.GOMAXPROCS(1)
	go func() {
		time.Sleep(time.Second)
		printstr("hello")
	}()

	go func() {
		for v := range c {
			fmt.Printf("讀取%+q\r\n", v)
		}
	}()
	for {
              ;
	}
}

channel的緩沖

無緩沖的channel

      無緩沖的channel unbuffered channel 是指在接收前沒有能力保存任何值的通道。這種類型的channel 要求發送端和接收端同時准備好,才能完成發送和接收操作。否則,通道會導致先執行發送或接收操作的阻塞等待。顧又稱為同步通信

  • 阻塞:由於某種原因數據沒有到達,當前協程(線程)持續處於等待狀態,直到條件滿足,才接觸阻塞。
  • 同步:在兩個或多個協程(線程)間,保持數據內容一致性的機制。

示例如上,寫了沒有讀會導致阻塞,讀了沒有寫會導致堵塞

有緩沖的channel

      有緩沖的通道(buffered channel)是一種在被接收前能存儲一個或者多個數據值的通道。這種類型的channel並不強制要求goroutine之間必須同時完成發送和接收。通道會阻塞發送和接收動作的條件也不同。

  • 只有channel通道中沒有要接收的值時,接收動作才會阻塞。
  • 只有通道沒有可用緩沖區容納被寫入(發送)的值時,發送動作才會阻塞。

      有緩沖的channel和無緩沖的channel之間的不同:無緩沖的channel保證進行發送和接收的 goroutine 會在同一時間進行數據交換;有緩沖的channel沒有這種保證。

示例

package main

import (
	"fmt"
	"runtime"
	"time"
)

var c = make(chan int32, 10)

func printstr(s string) {
	for _, value := range s {
		fmt.Printf("寫入%+q\r\n", value)
		c <- value
	}
}

func main() {
	runtime.GOMAXPROCS(1)
	go func() {

		printstr("hello")
	}()

	go func() {

		time.Sleep(time.Second * 2)
		fmt.Println("讀通道開始讀取數據")
		for v := range c {
			fmt.Printf("讀取%+q\r\n", v)
		}
	}()
	for {

	}
}

      結果可以看出,如果給定了一個緩沖區容量,channel就是異步的。只要緩沖區有未使用空間用於發送數據,或還包含可以接收的數據,那么其通信就會無阻塞地進行。

channel的關閉

當發送的一端沒有更多的數據發送到channel的話,需要使接收端也能及時知道channel中沒有多余的數據可以接收。因此可以通過 close()函數來關閉channel的實現。

示例

package main

import (
	"fmt"
	"runtime"
	"time"
)

var c = make(chan int32, 10)

func printstr(s string) {
	for _, value := range s {
		fmt.Printf("寫入%+q\r\n", value)
		c <- value
	}
	close(c)
}

func main() {
	runtime.GOMAXPROCS(1)
	go func() {
		printstr("hello")
	}()

	time.Sleep(time.Second * 2)
	fmt.Println("讀通道開始讀取數據")
	for {
		if char, ok := <-c; ok {
			fmt.Printf("讀取%+q\r\n", char)
		} else {
			break
		}
	}
}

提示

  • channel不像文件一樣需要經常去關閉,只有當你確實沒有任何發送數據了,或者你想顯式的結束range循環之類的,才去關閉channel;
  • 關閉channel后,無法向channel 再發送數據(引發 panic 錯誤后導致接收立即返回零值);
  • 關閉channel后,可以繼續從channel接收數據(讀取到的數據為channel類型的默認值,如int默認值0 string默認值"");
  • 對於nil channel,無論收發都會被阻塞。

緩沖channel 和 非緩沖channel的區別

  • 緩沖channel的創建方式為make(chan TYPE,CAPCTIY),非緩沖channel的創建方式為make(chan TYPE)
  • 緩沖channel的通信方式為同步通信,非緩沖channel的通信方式為異步通信

單項channel及應用

默認情況下,channel是雙向的,既可以往里面發送數據也可以接收數據。但是,常將channel作為參數進行傳遞而只希望對方是單向使用的,要么只讓它發送數據,要么只讓它接收數據,這時候可以指定通道的方向。

單項channel的聲明

  • 雙向channel ch = make(chan int)
  • 單向寫channel: var ch chan <- int ch = make(chan <- int)
  • 單向讀channel: var ch <- chan int ch = make(<-chan int)

可以將 channel 隱式轉換為單向隊列,只收或只發,不能將單向 channel 轉換為普通 channel,示例:

package main

import (
	"fmt"
	"runtime"
)

var c = make(chan string, 10)

func read(c <-chan string) {
	fmt.Println("讀通道開始讀取數據")
	for {
		if char, ok := <-c; ok {
			fmt.Printf("讀取%s\r\n", char)
		} else {
			break
		}
	}
}

func write(ch chan<- string, str []string) {
	defer close(ch)
	for _, value := range str {
		fmt.Printf("寫入%+q\r\n", value)
		ch <- value
	}
}

func main() {
	runtime.GOMAXPROCS(3)

	go write(c, []string{"h", "e", "l", "l", "o"})
	read(c)
}


免責聲明!

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



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