亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

golang使用通道時需要注意的一些問題

 更新時間:2023年07月04日 10:42:01   作者:自由de單車  
本文主要介紹了golang使用通道時需要注意的一些問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

環(huán)境

  • Go 1.20
  • Windows 11

常識

1.定義通道變量:

ch := make(chan int) // 可存放int類型數(shù)據(jù),緩沖為0
ch := make(chan any) // 可存放任意類型數(shù)據(jù),緩沖為0
ch := make(chan int, 5) // 存放int類型數(shù)據(jù),緩沖為5
// 默認的通道是既可以寫入又可以讀取的,但我們也可以限制通道的方向
ch := make(<-chan int) // 只能從此通道讀取數(shù)據(jù),且不能關(guān)閉此通道
ch := make(chan<- int) // 只能寫入數(shù)據(jù)到此通道
length := len(ch) // 通道里有多少個數(shù)據(jù)
capacity := cap(ch) // 通道的緩沖區(qū)大小

2.通道遵循FIFO先入先出規(guī)則,可以保證元素的順序

3.通道是并發(fā)安全的,不會因多個協(xié)程的同時寫入而發(fā)生數(shù)據(jù)錯亂

注意點

下面的代碼例子會經(jīng)常出現(xiàn)調(diào)用display函數(shù),這是我自己定義的一個函數(shù),主要用于打印信息,代碼如下:

func display(msg ...any) {
    fmt.Print(time.Now().Format(time.DateTime), " ")
    fmt.Println(msg...)
}

為了減少代碼冗余,下面的代碼例子就不再貼出此函數(shù)的代碼了。

1、對一個沒有關(guān)閉的通道進行讀寫時,如果遇上了阻塞,并且此時已經(jīng)沒有其它活躍(非阻塞)的協(xié)程在運行了,會報deadlock錯誤!

怎么理解這句話呢,首先要了解讀寫通道時什么情況下會阻塞:

  • 往緩沖已滿的通道寫入數(shù)據(jù)時會阻塞
  • 讀取空的通道會阻塞
  • 通道未初始化,例如var ch chan int就是未初始化的

針對第1點,假設(shè)通道緩沖是N,那么在第 N + 1 次寫入時會阻塞(定義通道變量時如果不指定N的大小,則N默認等于0)

針對第2點,如果這個空的通道是已關(guān)閉的,則不會阻塞,讀取到的是這個通道數(shù)據(jù)類型的零值

例子1:

func main() {
?? ?ch := make(chan int)
?? ?// 協(xié)程1
?? ?go func() {
?? ??? ?for i := 0; i < 3; i++ {
?? ??? ??? ?display("準備發(fā)送:", i)
?? ??? ??? ?ch <- i
?? ??? ??? ?display("已發(fā)送完畢:", i)
?? ??? ?}
?? ?}()
?? ?for data := range ch {
?? ??? ?display("獲得數(shù)據(jù):", data)
?? ?}
}

上面代碼運行后會報錯:fatal error: all goroutines are asleep - deadlock!

原因是,當【協(xié)程1】往通道寫入3個數(shù)據(jù)后,【協(xié)程1】就結(jié)束運行了,這時【main協(xié)程】(是的,main函數(shù)也是運行在協(xié)程里的)讀取出這3個數(shù)據(jù)后,并沒有退出for-range循環(huán),而是繼續(xù)讀取已空的ch通道,發(fā)生了阻塞,但這時只有【main協(xié)程】在運行了,只剩下一個協(xié)程,所以報錯。

例子1修改一下:

func main() {
?? ?ch := make(chan int)
?? ?// 協(xié)程1
?? ?go func() {
?? ??? ?for i := 0; i < 3; i++ {
?? ??? ??? ?display("準備發(fā)送:", i)
?? ??? ??? ?ch <- i
?? ??? ??? ?display("已發(fā)送完畢:", i)
?? ??? ?}
?? ?}()
? ? // 協(xié)程2
?? ?go func() {
?? ??? ?for data := range ch {
?? ??? ??? ?display("獲得數(shù)據(jù):", data)
?? ??? ?}
?? ?}()
? ? // 死循環(huán)
?? ?for {
?? ?}
}

經(jīng)修改后代碼不會再報錯了,原因是,【協(xié)程1】退出后,雖然【協(xié)程2】還在阻塞式地讀取空通道,但這時除了【協(xié)程2】以外,還有一個活躍的【main協(xié)程】在運行,所以不會報錯。

例子1再修改下:

func main() {
?? ?ch := make(chan int)
?? ?// 協(xié)程1
?? ?go func() {
?? ??? ?for i := 0; i < 3; i++ {
?? ??? ??? ?display("準備發(fā)送:", i)
?? ??? ??? ?ch <- i
?? ??? ??? ?display("已發(fā)送完畢:", i)
?? ??? ?}
?? ??? ?close(ch) // 新添加代碼
?? ?}()
?? ?for data := range ch {
?? ??? ?display("獲得數(shù)據(jù):", data)
?? ?}
}

協(xié)程1在寫入完所有數(shù)據(jù)后,使用close(ch)關(guān)閉了通道,這時也不會再報錯了。原因是,對于已關(guān)閉的通道,for-range循環(huán)讀取完通道的數(shù)據(jù)后,會自動結(jié)束循環(huán),不會阻塞在讀取通道處,所以不會報錯。

2、給一個已關(guān)閉的通道發(fā)送數(shù)據(jù),或者再次關(guān)閉一個已關(guān)閉的通道,會導致panic

這句話告訴我們,當發(fā)送方不再需要發(fā)送數(shù)據(jù)時,可以關(guān)閉通道,但不能讓接收方去關(guān)閉。
因為接收方并不知道發(fā)送方是否還需要發(fā)送數(shù)據(jù),如果胡亂關(guān)閉了通道,會導致發(fā)送方觸發(fā)panic

3、已關(guān)閉的通道是可以繼續(xù)讀取里面的數(shù)據(jù)的

func main() {
?? ?ch := make(chan int, 2)
?? ?ch <- 123
?? ?ch <- 456
?? ?close(ch)
?? ?// 使用for-range讀取已關(guān)閉通道,通道空了之后會自動跳出循環(huán)
?? ?for data := range ch {
?? ??? ?display(data)
?? ?}
?? ?// 方式2:使用ok變量判斷通道是否已空
?? ?/*for {
?? ??? ?data, ok := <-ch
?? ??? ?if !ok {
?? ??? ??? ?break
?? ??? ?}
?? ??? ?display(data)
?? ?}*/
? ? // 方式3:通過通道長度來判斷通道是否已空
?? ?/*num := len(ch)
?? ?for i := 0; i < num; i++ {
?? ??? ?data := <-ch
?? ??? ?display(data)
?? ?}*/
}

4、雙向通道可以傳遞給參數(shù)為單向通道的函數(shù)

// 函數(shù)參數(shù)是單向通道
func sendMessage(in chan<- int) {
?? ?for i := 0; i < 3; i++ {
?? ??? ?in <- i
?? ?}
?? ?close(in)
}
func main() {
?? ?ch := make(chan int) // 雙向通道
?? ?go sendMessage(ch)
?? ?for data := range ch {
?? ??? ?display(data)
?? ?}
}

5、當讀取通道與select搭配使用,并且設(shè)置了超時時間時,通道一定要設(shè)置緩沖

先看例子:

func sendMessage(in chan<- int, sleep time.Duration) {
?? ?time.Sleep(sleep)
?? ?in <- 1
}
func main() {
?? ?display("開始")
?? ?display("協(xié)程數(shù)量:", runtime.NumGoroutine())
?? ?ch1 := make(chan int) // 錯誤
?? ?// 正確:ch1 := make(chan int, 1)
? ? // 協(xié)程1
?? ?go sendMessage(ch1, 5 * time.Second)
?? ?select {
?? ?case v := <-ch1:
?? ??? ?display("從通道1獲取到了數(shù)據(jù):", v)
?? ?case <-time.After(1 * time.Second):
?? ??? ?display("超時了,退出select")
?? ?}
?? ?for {
?? ??? ?display("協(xié)程數(shù)量:", runtime.NumGoroutine())
?? ??? ?time.Sleep(1 * time.Second)
?? ?}
}

如上面代碼所示,一開始我們創(chuàng)建了一個無緩沖的通道ch1,然后開啟【協(xié)程1】,【協(xié)程1】在 5 秒后會往通道寫入一個數(shù)據(jù),但select的超時時間只設(shè)置了 1 秒。也就是說,在【協(xié)程1】往通道寫入數(shù)據(jù)前,select語句就已經(jīng)因為超時而結(jié)束了,此時的ch1通道已經(jīng)沒有接收方,只剩下發(fā)送方了。往一個無緩沖的通道寫入數(shù)據(jù)會導致【協(xié)程1】阻塞,而且沒有了接收方,【協(xié)程1】就會永遠阻塞下去,無法結(jié)束退出,從而導致協(xié)程泄露。

觀察超時后打印出來的協(xié)程數(shù)量,一直都是2,不會降低為1,也證實了上面的說法。所以在定義通道變量時,一定要設(shè)置緩沖區(qū)。

其實調(diào)高 select的超時時間,也能解決這個問題。但有時候我們可能無法得知協(xié)程具體的執(zhí)行耗時,從而預(yù)估出一個合理的超時時間,所以穩(wěn)妥起見,還是定義一個帶緩沖的通道比較好。

到此這篇關(guān)于golang使用通道時需要注意的一些問題的文章就介紹到這了,更多相關(guān)golang 通道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go語言中的二維切片賦值

    go語言中的二維切片賦值

    這篇文章主要介紹了go語言中的二維切片賦值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 詳解Go語言中自定義結(jié)構(gòu)體能作為map的key嗎

    詳解Go語言中自定義結(jié)構(gòu)體能作為map的key嗎

    在Go中,引用類型具有動態(tài)的特性,可能會被修改或指向新的數(shù)據(jù),這就引發(fā)了一個問題—能否將包含引用類型的自定義結(jié)構(gòu)體作為map的鍵呢,本文就來和大家想想講講
    2023-06-06
  • Go語言如何高效的進行字符串拼接(6種方式對比分析)

    Go語言如何高效的進行字符串拼接(6種方式對比分析)

    本文主要介紹了Go語言如何高效的進行字符串拼接(6種方式對比分析),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Go語言fsnotify接口實現(xiàn)監(jiān)測文件修改

    Go語言fsnotify接口實現(xiàn)監(jiān)測文件修改

    這篇文章主要為大家介紹了Go語言fsnotify接口實現(xiàn)監(jiān)測文件修改的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • GO中對map排序的實現(xiàn)

    GO中對map排序的實現(xiàn)

    本文主要介紹了GO中對map排序的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • Go語言中配置實現(xiàn)Logger日志的功能詳解

    Go語言中配置實現(xiàn)Logger日志的功能詳解

    當我們正式開發(fā)go程序的時候,就會發(fā)現(xiàn)記錄程序日志已經(jīng)不是fmt.print這么簡單了,所以我們需要專門的去存儲日志文件,這篇文章主要介紹了在Go語言中配置實現(xiàn)Logger日志的功能,感興趣的同學可以參考下文
    2023-05-05
  • go 壓縮解壓zip文件源碼示例

    go 壓縮解壓zip文件源碼示例

    這篇文章主要為大家介紹了go壓縮及解壓zip文件的源碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Go pprof內(nèi)存指標含義備忘錄及案例分析

    Go pprof內(nèi)存指標含義備忘錄及案例分析

    這篇文章主要介紹了Go pprof內(nèi)存指標含義備忘錄問題,小編特此把問題及案例分享到腳本之家平臺供大家學習,需要的朋友可以參考下
    2020-03-03
  • 使用?pprof?進行性能分析的方法詳解

    使用?pprof?進行性能分析的方法詳解

    pprof?是?Go?語言中用于性能分析的一個強大工具,它可以幫助開發(fā)人員找到應(yīng)用程序中的性能瓶頸,并提供詳細的分析報告,本文將介紹如何使用?pprof?進行性能分析,需要的朋友可以參考下
    2023-05-05
  • go kratos源碼及配置解析

    go kratos源碼及配置解析

    這篇文章主要為大家介紹了go kratos源碼及配置解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論