欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang使用通道時(shí)需要注意的一些問(wèn)題

 更新時(shí)間:2023年07月04日 10:42:01   作者:自由de單車(chē)  
本文主要介紹了golang使用通道時(shí)需要注意的一些問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

環(huán)境

  • Go 1.20
  • Windows 11

常識(shí)

1.定義通道變量:

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

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

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

注意點(diǎn)

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

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

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

1、對(duì)一個(gè)沒(méi)有關(guān)閉的通道進(jìn)行讀寫(xiě)時(shí),如果遇上了阻塞,并且此時(shí)已經(jīng)沒(méi)有其它活躍(非阻塞)的協(xié)程在運(yùn)行了,會(huì)報(bào)deadlock錯(cuò)誤!

怎么理解這句話呢,首先要了解讀寫(xiě)通道時(shí)什么情況下會(huì)阻塞:

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

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

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

例子1:

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

上面代碼運(yùn)行后會(huì)報(bào)錯(cuò):fatal error: all goroutines are asleep - deadlock!

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

例子1修改一下:

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

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

例子1再修改下:

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

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

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

這句話告訴我們,當(dāng)發(fā)送方不再需要發(fā)送數(shù)據(jù)時(shí),可以關(guān)閉通道,但不能讓接收方去關(guān)閉。
因?yàn)榻邮辗讲⒉恢腊l(fā)送方是否還需要發(fā)送數(shù)據(jù),如果胡亂關(guān)閉了通道,會(huì)導(dǎo)致發(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ì)自動(dòng)跳出循環(huán)
?? ?for data := range ch {
?? ??? ?display(data)
?? ?}
?? ?// 方式2:使用ok變量判斷通道是否已空
?? ?/*for {
?? ??? ?data, ok := <-ch
?? ??? ?if !ok {
?? ??? ??? ?break
?? ??? ?}
?? ??? ?display(data)
?? ?}*/
? ? // 方式3:通過(guò)通道長(zhǎng)度來(lái)判斷通道是否已空
?? ?/*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、當(dāng)讀取通道與select搭配使用,并且設(shè)置了超時(shí)時(shí)間時(shí),通道一定要設(shè)置緩沖

先看例子:

func sendMessage(in chan<- int, sleep time.Duration) {
?? ?time.Sleep(sleep)
?? ?in <- 1
}
func main() {
?? ?display("開(kāi)始")
?? ?display("協(xié)程數(shù)量:", runtime.NumGoroutine())
?? ?ch1 := make(chan int) // 錯(cuò)誤
?? ?// 正確: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("超時(shí)了,退出select")
?? ?}
?? ?for {
?? ??? ?display("協(xié)程數(shù)量:", runtime.NumGoroutine())
?? ??? ?time.Sleep(1 * time.Second)
?? ?}
}

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

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

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

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

相關(guān)文章

  • go語(yǔ)言中的二維切片賦值

    go語(yǔ)言中的二維切片賦值

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

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

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

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

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

    Go語(yǔ)言fsnotify接口實(shí)現(xiàn)監(jiān)測(cè)文件修改

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

    GO中對(duì)map排序的實(shí)現(xiàn)

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

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

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

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

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

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

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

    使用?pprof?進(jìn)行性能分析的方法詳解

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

    go kratos源碼及配置解析

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

最新評(píng)論