日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

通道(channel)

發(fā)布時(shí)間:2023/12/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 通道(channel) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

描述

  • 主要用于多個(gè)goroutine間傳遞數(shù)據(jù).一個(gè)通道相當(dāng)于一個(gè)先進(jìn)先出(FIFO)的隊(duì)列.
  • channel用來在協(xié)程[goroutine]之前傳遞數(shù)據(jù),準(zhǔn)確的說,是用來傳遞數(shù)據(jù)的所有權(quán)。
  • 一個(gè)設(shè)計(jì)良好的程序應(yīng)該確保同一時(shí)刻channel里面的數(shù)據(jù)只會被同一個(gè)協(xié)程擁有,這樣就可以避免并發(fā)帶來的數(shù)據(jù)不安全問題[data races]。
  • 官方的go編譯器限制channel最多能容納到65535個(gè)元素,但我們不宜傳遞體積過大的元素值,因?yàn)閏hannel的數(shù)據(jù)從進(jìn)入到流出會涉及到數(shù)據(jù)拷貝操作。如果元素體積過大,最好的方法還是使用傳遞指針來取代傳遞值。

內(nèi)部結(jié)構(gòu)

每個(gè)channel內(nèi)部實(shí)現(xiàn)都有三個(gè)隊(duì)列。

  • 接收消息的協(xié)程隊(duì)列。
    這個(gè)隊(duì)列的結(jié)構(gòu)是一個(gè)限定最大長度的鏈表,所有阻塞在channel的接收操作的協(xié)程都會被放在這個(gè)隊(duì)列里。
  • 發(fā)送消息的協(xié)程隊(duì)列。
    這個(gè)隊(duì)列的結(jié)構(gòu)也是一個(gè)限定最大長度的鏈表。所有阻塞在channel的發(fā)送操作的協(xié)程也都會被放在這個(gè)隊(duì)列里。
  • 環(huán)形數(shù)據(jù)緩沖隊(duì)列。
    這個(gè)環(huán)形數(shù)組的大小就是channel的容量。如果數(shù)組裝滿了,就表示channel滿了,如果數(shù)組里一個(gè)值也沒有,就表示channel是空的。
  • 對于一個(gè)阻塞型channel來說,它總是同時(shí)處于即滿又空的狀態(tài)。一個(gè)channel被所有使用它的協(xié)程所引用,也就是說,只要這兩個(gè)裝了協(xié)程的隊(duì)列長度大于零,那么這個(gè)channel就永遠(yuǎn)不會被垃圾回收。另外,協(xié)程本身如果阻塞在channel的讀寫操作上,這個(gè)協(xié)程也永遠(yuǎn)不會被垃圾回收。

分類

  • 非緩沖通道
    make的時(shí)候第二個(gè)參數(shù)為0或者不填.
    無論是發(fā)送操作還是接收操作,一開始執(zhí)行就會被阻塞,直到配對的操作也開始執(zhí)行才會繼續(xù)傳遞。由此可見,非緩沖通道是在用同步的方式傳遞數(shù)據(jù)。也就是說,只有收發(fā)雙方對接上了,數(shù)據(jù)才會被傳遞.數(shù)據(jù)是直接從發(fā)送方復(fù)制到接收方的,中間并不會用非緩沖通道做中轉(zhuǎn)
package mainimport ("fmt""time" )func main() {ch1 := make(chan int) //非緩沖信道go func() {ch1 <- 1fmt.Println("after 5 seconds, output this") //阻塞5秒后,主線程ch1信道才被接收,才輸出該語句}()time.Sleep(5 * time.Second)<-ch1time.Sleep(2 * time.Second) //防止主線程結(jié)束,關(guān)閉其他線程 }
  • 緩沖通道
    make 的時(shí)候第一個(gè)參數(shù)大于0.
    在有容量的時(shí)候,發(fā)送和接收是不會互相依賴的.用異步的方式傳遞數(shù)據(jù)。
package mainimport ( "fmt" "time" )func main() {ch1 := make(chan int, 1) //緩沖信道go func() {ch1 <- 1fmt.Println("not after 5 seconds, output this")}()time.Sleep(5 * time.Second)fmt.Println(<-ch1) }

特性

  • 是類型安全的
  • 發(fā)送操作之間是互斥的,接收操作之間也是互斥的(多線程安全)
  • 進(jìn)入通道的并不是在接收操作符右邊的那個(gè)元素值,而是它的副本
  • 移出通道的是通道元素的副本
  • channel關(guān)閉后,如果還有數(shù)據(jù)還是可以正常讀取的,等讀取完后,v,ok := <- ch,其中ok是false,v是對應(yīng)類型的零值
package mainimport ("fmt""time" )func main() {ch1 := make(chan int, 5) //緩沖信道go func() {for i := 0; i < 5; i++ {ch1 <- iif i >= 4 {close(ch1) }}}()time.Sleep(5 * time.Second)//保證上個(gè)goroutine結(jié)束,即ch1是關(guān)閉的i := 0for {v,ok := <-ch1fmt.Println(v,ok)i++if i > 7 { //若不加這段代碼,會一直讀break}} } 輸出: 0 true 1 true 2 true 3 true 4 true 0 false 0 false 0 false
  • 通道會阻塞goroutine
  • 關(guān)閉一個(gè)只讀channel是非法的,編譯器直接報(bào)錯(cuò)
  • 使用 v, ok <- ch 接收一個(gè)值。第二個(gè)遍歷ok是可選的,它表示channel是否已關(guān)閉。接收值只會又兩種結(jié)果,要么成功要么阻塞,而永遠(yuǎn)也不會引發(fā)panic
  • 不同于array/slice/map上的for-range,channel的for-range只允許有一個(gè)變量
package mainimport ("fmt" )func main() {ch1 := make(chan int, 5) //緩沖信道go func() {for i := 0; i < 5; i++ {ch1 <- iif i >= 4 {close(ch1) //如果不加這句,會因?yàn)閞ange一直讀一個(gè)未關(guān)閉channel,而死鎖}}}()for v := range ch1 { //一直接收channel數(shù)據(jù),直到channel關(guān)閉fmt.Println(v)} }

阻塞的Case

  • 通道容量已滿情況,執(zhí)行寫入
  • 通道沒有數(shù)據(jù),執(zhí)行讀取
  • 對于值為nil的通道,不論它的具體類型是什么,對它的發(fā)送操作和接收操作都會永久地處于阻塞狀態(tài)。它們所屬的 goroutine 中的任何代碼,都不再會被執(zhí)行.
  • 對于值為nil的通道,若程序只有一個(gè)goroutine,向其中發(fā)送數(shù)據(jù)或接收數(shù)據(jù),都會阻塞,程序會死鎖;若程序有多個(gè)goroutine,只會阻塞那個(gè)向nil通道發(fā)送或接收數(shù)據(jù)的goroutine,程序不會panic

panic的Case

  • 關(guān)閉已經(jīng)關(guān)閉的channel
  • 已經(jīng)關(guān)閉的channel發(fā)送數(shù)據(jù)

單向通道

make(chan<- int, 1) make(<-chan int, 1)
  • 作用
    a.作為函數(shù)參數(shù),從而約束函數(shù)體內(nèi)的行為;
    b.作為函數(shù)返回值,從而約束返回后的行為;
//函數(shù)返回一個(gè)單向通道,那么該通道只能接收,不能發(fā)送 func getIntChan() <-chan int {ch := make(chan int, 5)for i := 0; i < 5; i++ {ch <- i}close(ch)return ch } func main() {ch := getIntChan()ch <- 6 //編譯報(bào)錯(cuò)send to receive-only type <-chan int }

正常通道關(guān)閉

  • 從channel的接收協(xié)程隊(duì)列中移除所有的goroutine,并喚醒它們。

  • 從channel的接收協(xié)程隊(duì)列中移除所有的goroutine,并喚醒它們。

  • 一個(gè)已關(guān)閉的channel內(nèi)部的緩沖數(shù)組可能不是空的,沒有接收的這些值會導(dǎo)致channel對象永遠(yuǎn)不會被垃圾回收。

總結(jié)

  • 如果channel關(guān)閉了,那么它的接收和發(fā)送協(xié)程隊(duì)列必然空了,但是它的緩沖數(shù)組可能還沒有空。
  • channel的接收協(xié)程隊(duì)列和緩沖數(shù)組,同一個(gè)時(shí)間必然有一個(gè)是空的
  • channel的緩沖數(shù)組如果未滿,那么它的發(fā)送協(xié)程隊(duì)列必然是空的
  • 對于緩沖型channel,同一時(shí)間它的接收和發(fā)送協(xié)程隊(duì)列,必然有一個(gè)是空的
  • 對于非緩沖型channel,一般來說同一時(shí)間它的接收和發(fā)送協(xié)程隊(duì)列,也必然有一個(gè)是空的,但是有一個(gè)例外,那就是當(dāng)它的發(fā)送操作和接收操作在同一個(gè)select塊里出現(xiàn)的時(shí)候,兩個(gè)隊(duì)列都不是空的。

總結(jié)

以上是生活随笔為你收集整理的通道(channel)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。