使用簡單的 make 調用創建的通道叫做無緩沖通道,但 make 還可以接受第二個可選參數,一個表示通道容量的整數。如果容量是 0,make 創建一個無緩沖通道。

成都網站設計、做網站,成都做網站公司-創新互聯建站已向1000多家企業提供了,網站設計,網站制作,網絡營銷等服務!設計與技術結合,多年網站推廣經驗,合理的價格為您打造企業品質網站。
無緩沖通道上的發送操作將被阻塞,直到另一個 goroutine 在對應的通道上執行接受操作,這時值傳送完成,兩個 goroutine 都可以繼續執行。相反,如果接受操作先執行,接收方 goroutine 將阻塞,直到另一個 goroutine 在同一個通道上發送一個值。使用無緩沖通道進行的通信導致發送和接受操作 goroutine 同步化。因此,無緩沖通道也稱為同步通道。當一個值在無緩沖通道上傳遞時,接受值后發送方 goroutine 才能被喚醒。
緩沖通道上的發送操作在隊列的尾部插入一個元素,接收操作從隊列的頭部移除一個元素。如果通道滿了,發送操作會阻塞所在的 goroutine 直到另一個 goroutine 對它進行接收操作來留出可用的空間。反過來,如果通道是空的,執行接收操作的 goroutine 阻塞,直到另一個 goroutine 在通道上發送數據。
如果給一個 nil 的 channel 發送數據,會造成永遠阻塞。
如果從一個 nil 的 channel 中接收數據,也會造成永久阻塞。 給一個已經關閉的 channel 發送數據, 會引起 panic。
從一個已經關閉的 channel 接收數據, 如果緩沖區中為空,則返回一個 零 值。
本文是對 Gopher 2017 中一個非常好的 Talk?: [Understanding Channel](GopherCon 2017: Kavya Joshi - Understanding Channels) 的學習筆記,希望能夠通過對 channel 的關鍵特性的理解,進一步掌握其用法細節以及 Golang 語言設計哲學的管窺蠡測。
channel 是可以讓一個 goroutine 發送特定值到另一個 gouroutine 的通信機制。
原生的 channel 是沒有緩存的(unbuffered channel),可以用于 goroutine 之間實現同步。
關閉后不能再寫入,可以讀取直到 channel 中再沒有數據,并返回元素類型的零值。
gopl/ch3/netcat3
首先從 channel 是怎么被創建的開始:
在 heap 上分配一個 hchan 類型的對象,并將其初始化,然后返回一個指向這個 hchan 對象的指針。
理解了 channel 的數據結構實現,現在轉到 channel 的兩個最基本方法: sends 和 receivces ,看一下以上的特性是如何體現在 sends 和 receives 中的:
假設發送方先啟動,執行 ch - task0 :
如此為 channel 帶來了 goroutine-safe 的特性。
在這樣的模型里, sender goroutine - channel - receiver goroutine 之間, hchan 是唯一的共享內存,而這個唯一的共享內存又通過 mutex 來確保 goroutine-safe ,所有在隊列中的內容都只是副本。
這便是著名的 golang 并發原則的體現:
發送方 goroutine 會阻塞,暫停,并在收到 receive 后才恢復。
goroutine 是一種 用戶態線程 , 由 Go runtime 創建并管理,而不是操作系統,比起操作系統線程來說,goroutine更加輕量。
Go runtime scheduler 負責將 goroutine 調度到操作系統線程上。
runtime scheduler 怎么將 goroutine 調度到操作系統線程上?
當阻塞發生時,一次 goroutine 上下文切換的全過程:
然而,被阻塞的 goroutine 怎么恢復過來?
阻塞發生時,調用 runtime sheduler 執行 gopark 之前,G1 會創建一個 sudog ,并將它存放在 hchan 的 sendq 中。 sudog 中便記錄了即將被阻塞的 goroutine G1 ,以及它要發送的數據元素 task4 等等。
接收方 將通過這個 sudog 來恢復 G1
接收方 G2 接收數據, 并發出一個 receivce ,將 G1 置為 runnable :
同樣的, 接收方 G2 會被阻塞,G2 會創建 sudoq ,存放在 recvq ,基本過程和發送方阻塞一樣。
不同的是,發送方 G1如何恢復接收方 G2,這是一個非常神奇的實現。
理論上可以將 task 入隊,然后恢復 G2, 但恢復 G2后,G2會做什么呢?
G2會將隊列中的 task 復制出來,放到自己的 memory 中,基于這個思路,G1在這個時候,直接將 task 寫到 G2的 stack memory 中!
這是違反常規的操作,理論上 goroutine 之間的 stack 是相互獨立的,只有在運行時可以執行這樣的操作。
這么做純粹是出于性能優化的考慮,原來的步驟是:
優化后,相當于減少了 G2 獲取鎖并且執行 memcopy 的性能消耗。
channel 設計背后的思想可以理解為 simplicity 和 performance 之間權衡抉擇,具體如下:
queue with a lock prefered to lock-free implementation:
比起完全 lock-free 的實現,使用鎖的隊列實現更簡單,容易實現
無緩沖的通道(unbuffered channel)是指在接收前沒有能力保存任何值的通道。
這種類型的通道要求發送goroutine和接收goroutine同時準備好,才能完成發送和接收操作。否則,通道會導致先執行發送或接收操作的 goroutine 阻塞等待。
這種對通道進行發送和接收的交互行為本身就是同步的。其中任意一個操作都無法離開另一個操作單獨存在。
阻塞:由于某種原因數據沒有到達,當前協程(線程)持續處于等待狀態,直到條件滿足,才接觸阻塞。
同步:在兩個或多個協程(線程)間,保持數據內容一致性的機制。
下圖展示兩個 goroutine 如何利用無緩沖的通道來共享一個值:
在第 1 步,兩個 goroutine 都到達通道,但哪個都沒有開始執行發送或者接收。
在第 2 步,左側的 goroutine 將它的手伸進了通道,這模擬了向通道發送數據的行為。這時,這個 goroutine 會在通道中被鎖住,直到交換完成。
在第 3 步,右側的 goroutine 將它的手放入通道,這模擬了從通道里接收數據。這個 goroutine 一樣也會在通道中被鎖住,直到交換完成。
在第 4 步和第 5 步,進行交換,并最終,在第 6 步,兩個 goroutine 都將它們的手從通道里拿出來,這模擬了被鎖住的 goroutine 得到釋放。兩個 goroutine 現在都可以去做別的事情了。
如果沒有指定緩沖區容量,那么該通道就是同步的,因此會阻塞到發送者準備好發送和接收者準備好接收。
無緩沖channel: —— 同步通信
1、給一個nil channel發送數據,造成永遠阻塞
2、從一個nil channel接收數據,造成永遠阻塞
3、給一個已經關閉的channel發送數據,引起panic
4、從一個已經關閉的channel接收數據,如果緩沖區中為空,則返回一個零值
5、無緩沖的channel是同步的,而有緩沖的channel是非同步的
網站名稱:channelgo語言 go channel 通信
文章位置:http://www.yijiale78.com/article4/hhcjie.html
成都網站建設公司_創新互聯,為您提供定制網站、網站收錄、微信公眾號、營銷型網站建設、建站公司、網站維護
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯