網站首頁 編程語言 正文
Channel 詳解
Channel 簡要說明
Channel(一般簡寫為 chan) 管道提供了一種機制,它在兩個并發執行的協程之間進行同步,并通過傳遞與該管道元素類型相符的值來進行通信。Channel 是用來在不同的 goroutine 中交換數據的,千萬不要把 Channel 拿來在同一個 goroutine 中的不同函數之間間交換數據,chan 可以理解為一個管道或者先進先出的隊列。
Channel 類型定義
最簡單形式: chan elementType
,通過這個類型的值,你可以發送和接收elementType 類型的元素。Channel 是引用類型,如果將一個 chan 變量賦值給另外一個,則這兩個變量訪問的是相同的 chann。
當然,我們可以用 make 分配一個channel:var c = make(chan int)
Channel 操作符<-
和操作方式
通信操作符 <-
的箭頭指示數據流向,箭頭指向哪里,數據就流向哪里,它是一個二元操作符,可以支持任意類型,對于 channel 的操作只有4種方式:
1.創建 channel (通過make()函數實現,包括無緩存 channel 和有緩存 channel);
2.向 channel 中添加數據(channel<-data);
3.從 channel 中讀取數據(data<-channel);
- data<-channel, 從 channel 中接收數據并賦值給 data
- <-channel,從 channel 中接收數據并丟棄
4.關閉 channel(通過 close()函數實現)
- 讀取關閉后的無緩存通道,不管通道中是否有數據,返回值都為 0 和 false。
- 讀取關閉后的有緩存通道,將緩存數據讀取完后,再讀取返回值為 0 和 false。
- 對于一個關閉的 channel,如果繼續向 channel 發送數據,會引起 panic
- channel 不能 close 兩次,多次 close 會 panic
Channel 有無緩沖 & 同步、異步
channel 分為有緩沖 channel 和無緩沖 channel,兩種 channel 的創建方法如下:
var ch = make(chan int) //無緩沖 channel,等同于make(chan int ,0),是一個同步的 Channel
- 無緩沖 channel 在讀和寫的過程中是都會阻塞,由于阻塞的存在,所以使用 channel 時特別注意使用方法,防止死鎖和協程泄漏的產生。
- 無緩沖 channel 的發送動作一直要到有一個接收者接收這個值才算完成,否則都是阻塞著的,也就是說,發送的數據需要被讀取后,發送才會完成
- 一般要配合 select + timeout 處理,然后再在這里添加超時時間
var ch = make(chan int,10) //有緩沖channel,緩沖大小是10,是一個異步的Channel
- 帶緩存的 channel 實際上是一個阻塞隊列。隊列滿時寫協程會阻塞,隊列空時讀協程阻塞。
- 有緩沖的時候,寫操作是寫完之后直接返回的。相對于不帶緩存 channel,帶緩存 channel 不易造成死鎖。
Channel 各種操作導致阻塞和協程泄漏的場景
寫操作,什么時候會被阻塞?
1.向 nil 通道發送數據會被阻塞
2.向無緩沖 channel 寫數據,如果讀協程沒有準備好,會阻塞
- 無緩沖 channel ,必須要有讀有寫,寫了數據之后,必須要讀出來,否則導致 channel 阻塞,從而使得協程阻塞而使得協程泄漏
- 一個無緩沖 channel,如果每次來一個請求就開一個 go 協程往里面寫數據,但是一直沒有被讀取,那么就會導致這個 chan 一直阻塞,使得寫這個 chan 的 go 協程一直無法釋放從而協程泄漏。
3.向有緩沖 channel 寫數據,如果緩沖已滿,會阻塞
有緩沖的 channel,在緩沖 buffer 之內,不讀取也不會導致阻塞,當然也就不會使得協程泄漏,但是如果寫數據超過了 buffer 還沒有讀取,那么繼續寫的時候就會阻塞了。如果往有緩沖的 channel 寫了數據但是一直沒有讀取就直接退出協程的話,一樣會導致 channel 阻塞,從而使得協程阻塞并泄漏。
讀操作,什么時候會被阻塞?
- 從 nil 通道接收數據會被阻塞
- 從無緩沖 channel 讀數據,如果寫協程沒有準備好,會阻塞
- 從有緩沖 channel 讀數據,如果緩沖為空,會阻塞
close 操作,什么時候會被阻塞?
close channel 對 channel 阻塞是沒有任何效果的,寫了數據但是不讀,直接 close,還是會阻塞的。
Channel 各種操作對應的狀態
- 正常的 channel,可讀、可寫
- nil 的 channel,表示未初始化的狀態,只進行了聲明,或者手動賦值為 nil
- 已經 closed 的 channel,表示已經 close 關閉了,千萬不要誤認為關閉 channel 后,channel 的值是 nil
Channel 長度和容量
容量(capacity)代表 Channel 容納的最多的元素的數量,代表Channel的緩存的大小。如果沒有設置容量,或者容量設置為0, 說明 Channel 沒有緩存,長度和容量的兩個函數是 cap 和 len 。
示例如下:
c := make(chan int, 100) // cap 就是 100,但是此時 len 為 0 c <- 0 // len = 1, cap = 100 c <- 0 // len = 2, cap = 100 <- c // len = 1, cap = 100
Channel 的缺點
Channel 的缺點:
Channel 可能會導致循環阻塞或者協程泄漏,這個是最最最要重點關注的。
Channel 中傳遞指針會導致數據競態問題(data race/ race conditions)
Channel 中傳遞的都是數據的拷貝,可能會影響性能,但是就目前我們的機器性能來看,這點數據拷貝所帶來的 CPU 消耗,大多數的情況下可以忽略。
Go Channel 實現協程同步
channel 實現并發同步的說明
channel 作為 Go 并發模型的核心思想:不要通過共享內存來通信,而應該通過通信來共享內存,那么在 Go 里面,當然也可以很方便通過 channel 來實現協程的并發和同步了,并且 channel 本身還可以支持有緩沖和無緩沖的,通過 channel + timeout 實現并發協程之間的同步也是常見的一種使用姿勢。
無緩沖 chan 示例
示例如下:
package main import "fmt" func main() { var ch = make(chan string) for i := 0; i < 10; i++ { go sum(i, i+10, ch) } for i := 0; i < 10; i++ { fmt.Print(<-ch) } } func sum(start, end int, ch chan string) { var sum int = 0 for i := start; i < end; i++ { sum += i } ch <- fmt.Sprintf("Sum from %d to %d is %d\n", start, end, sum) }
有緩沖 chan 示例
message_chan := make(chan int, 2) go func() { time.Sleep(time.Second * 3) println("start recv...") println(<-message_chan) println(<-message_chan) println(<-message_chan) println("finish recv...") }() println("start send 10...") message_chan <- 10 println("start send 20...") message_chan <- 20 println("start send 30...") message_chan <- 30 println("finish send...") time.Sleep(time.Second * 3) close(message_chan)
原文鏈接:https://juejin.cn/post/7168842332723216420
相關推薦
- 2022-09-22 哈希思想的經典應用(位圖,哈希切割)
- 2022-09-14 Android?實現卡片堆疊錢包管理動畫效果_Android
- 2023-06-19 Linux下使用Shell腳本實現進程監控的流程_linux shell
- 2022-07-04 反向傳播BP學習算法Gradient?Descent的推導過程_相關技巧
- 2023-03-27 python去除空格,tab制表符和\n換行符的小技巧分享_python
- 2022-07-02 C#并行編程之Task任務_C#教程
- 2022-03-14 npm run dev報錯如何解決,找了這么久也許這里有你想要的答案
- 2022-10-04 Android實現圓圈倒計時_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支