網站首頁 編程語言 正文
一、通道是什么?
其實無論是原子函數還是共享鎖都是通過共享內存的方式進行的同步、效率一般不高,而Go語言中則使用了通道,它是一種通過傳遞信息的方式進行數據同步,通過發送和接收需要共享的資源,在goroutine 之間做同步。可以把通道看作是Goroutine之間的橋梁。
例1:創建一個通道
// 無緩沖的整型通道 unbuffered := make(chan int) // 有緩沖的字符串通道 buffered := make(chan string, 10)
通道分為有緩沖和無緩沖的通道。
創建一個Channel的關鍵點:1.使用make創建 2.使用chan來告訴make我要創建的是通道 3.要告訴通道我要建立什么類型的通道。
例2:向通道發送值和接受值
// 有緩沖的字符串通道 buffered := make(chan string, 10) // 通過通道發送一個字符串 buffered <- "Gopher" // 從通道接收一個字符串 value := <-buffered
這個例子中創建了一個string類型的Channel,并向通道內傳遞了一個“Gopher”字符串,這里是通過<-進行傳入的,然后通過<-這個方式把值放到value當中。
這里我的理解 <-就好比是一個賦值符號,無論是把值傳遞到Channel中,還是把Channel中的值傳出來,都是將右邊的值給左邊
二、通道的種類
由上面的例如1,可以看到Channel也是有多種的,分為無緩沖通道和有緩沖通道,下面就簡單總結一下兩種類型的通道。
1.無緩沖通道
無緩沖的通道(unbuffered channel)是指在接收前沒有能力保存任何值的通道。這種類型的通道要求發送goroutine 和接收goroutine 同時準備好,才能完成發送和接收操作。
上面的圖很好的解釋了通道和Goroutine的關系
- 1.左右兩個goroutine都沒有將手放到通道中。
- 2.左邊的Goroutine將手放到了通道中,模擬了將數據放入通道,此時goroutine會被鎖住
- 3.右邊的Goroutine也將手放到了通道中,模擬了從通道中取出數據,同樣進入了通道也會被鎖住
- 4.兩者通過通道執行數據的交換
- 5.交換完成
- 6.兩者將手從通道中拿出,模擬了被鎖住的goroutine被釋放
下面這個程序,模擬了兩個人打網球,很好的模擬了兩個協程間通過channel進行數據交換
package ChannelDemo import ( "fmt" "math/rand" "sync" "time" ) var wg sync.WaitGroup func init() { rand.Seed(time.Now().UnixNano()) } func PlayTennis() { court := make(chan int) wg.Add(2) //啟動了兩個協程,一個納達爾一個德約科維奇 go player("納達爾", court) go player("德約科維奇", court) //將1放到通道中,模擬開球 court <- 1 wg.Wait() } func player(name string, court chan int) { defer wg.Done() for { // 將數據從通道中取出 ball, ok := <-court if !ok { fmt.Printf("選手 %s 勝利\n", name) return } //獲取一個隨機值,如果可以整除13,就讓一個人沒有擊中,進而關閉整個通道 n := rand.Intn(100) if n%13 == 0 { fmt.Printf("選手 %s 沒接到\n", name) close(court) return } //如果擊中球,就將擊球的數量+1,放回通道中 fmt.Printf("選手 %s 擊中 %d\n", name, ball) ball++ court <- ball } }
執行結果(每次會有變化):
選手 納達爾 擊中 1
選手 德約科維奇 擊中 2
選手 納達爾 擊中 3
選手 德約科維奇 擊中 4
選手 納達爾 擊中 5
選手 德約科維奇 擊中 6
選手 納達爾 擊中 7
選手 德約科維奇 擊中 8
選手 納達爾 沒接到
選手 德約科維奇 勝利
ok 標志是否為false。如果這個值是false,表示通道已經被關閉,游戲結束。
下面這個例子,模擬里一個接力賽,也就是協程之間的傳遞的另一種形式
package ChannelDemo import ( "fmt" "sync" "time" ) var runnerWg sync.WaitGroup func Running() { //創建一個“接力棒”,也就是通道 baton := make(chan int) runnerWg.Add(1) //創建第一個跑步走 go Runner(baton) //開始跑 baton <- 1 runnerWg.Wait() } func Runner(baton chan int) { var newRunner int //選手接過接力棒 runner := <-baton fmt.Printf("第 %d 選手接棒 \n", runner) //如果不是第四名選手,那么說明比賽還在繼續 if runner != 4 { //創建一名新選手 newRunner = runner + 1 fmt.Printf("第 %d 準備接棒 \n", newRunner) go Runner(baton) } //模擬跑步 time.Sleep(100 * time.Millisecond) //如果第四名跑完了,就結束 if runner == 4 { fmt.Printf("第 %d 結束賽跑 \n", runner) runnerWg.Done() return } fmt.Printf("第 %d 選手和第 %d 選手交換了接力棒 \n", runner, newRunner) //選手遞出接力棒 baton <- newRunner }
運行結果:
第 1 名選手接棒
第 2 名選手準備接棒
第 1 名選手將接力棒遞給第 2 名選手
第 2 名選手接棒
第 3 名選手準備接棒
第 2 名選手將接力棒遞給第 3 名選手
第 3 名選手接棒
第 4 名選手準備接棒
第 3 名選手將接力棒遞給第 4 名選手
第 4 名選手接棒
第 4 名選手沖線,比賽結束?
三、無緩沖通道小結
我在看例子的過程中,其實遇到的問題在于,我沒有理解goroutine是怎么進行交換的,我以為是goroutine有一個集合一樣的結構在通道外面等待取數據,這樣就存在我剛拿完再那的情況。就像下面這個圖顯示一樣
但是實際情況應該像下面
Go1寫入通道鎖住的Go1、Go2讀出進入通道鎖住Go2,只有Go1寫完Go2取完才能釋放,但是像上面第一個例子代碼,讀出之后馬上就寫入,所以對于這樣的協程其實一直是鎖住的狀態。兩個協程就通過這種方式進行數據的傳遞。
原文鏈接:https://www.cnblogs.com/dcz2015/p/10383881.html
相關推薦
- 2022-09-18 centos+nginx+uwsgi部署django項目上線_python
- 2022-05-27 C++?二叉樹的實現超詳細解析_C 語言
- 2022-10-01 django中資源文件夾的引入及配置方法_python
- 2022-04-24 C#中的==運算符用法講解_C#教程
- 2022-09-15 Python?并行化執行詳細解析_python
- 2022-06-08 Spring Boot Start之mqtt框架封裝
- 2022-07-11 MongoDB使用正則匹配、修改內容
- 2022-10-14 yum 倉庫管理 yum-config-manager
- 最近更新
-
- 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同步修改后的遠程分支