最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

GoLang?channel關(guān)閉狀態(tài)相關(guān)操作詳解

 更新時間:2022年10月11日 10:50:04   作者:~龐貝  
Channel?和?goroutine?的結(jié)合是?Go?并發(fā)編程的大殺器。而?Channel?的實際應(yīng)用也經(jīng)常讓人眼前一亮,通過與?select,cancel,timer?等結(jié)合,它能實現(xiàn)各種各樣的功能。接下來,我們就要介紹GoLang?channel關(guān)閉狀態(tài)相關(guān)操作

關(guān)于 channel 的使用,有幾點不方便的地方:

1.在不改變 channel 自身狀態(tài)的情況下,無法獲知一個 channel 是否關(guān)閉。

2.關(guān)閉一個 closed channel 會導(dǎo)致 panic。所以,如果關(guān)閉 channel 的一方在不知道 channel 是否處于關(guān)閉狀態(tài)時就去貿(mào)然關(guān)閉 channel 是很危險的事情。

3.向一個 closed channel 發(fā)送數(shù)據(jù)會導(dǎo)致 panic。所以,如果向 channel 發(fā)送數(shù)據(jù)的一方不知道 channel 是否處于關(guān)閉狀態(tài)時就去貿(mào)然向 channel 發(fā)送數(shù)據(jù)是很危險的事情。

一個比較粗糙的檢查 channel 是否關(guān)閉的函數(shù):

func IsClosed(ch <-chan T) bool {
	select {
	case <-ch:
		return true
	default:
	}
	return false
}
func main() {
	c := make(chan T)
	fmt.Println(IsClosed(c)) // false
	close(c)
	fmt.Println(IsClosed(c)) // true
}

看一下代碼,其實存在很多問題。首先,IsClosed 函數(shù)是一個有副作用的函數(shù)。每調(diào)用一次,都會讀出 channel 里的一個元素,改變了 channel 的狀態(tài)。這不是一個好的函數(shù),干活就干活,還順手牽羊!

其次,IsClosed 函數(shù)返回的結(jié)果僅代表調(diào)用那個瞬間,并不能保證調(diào)用之后會不會有其他 goroutine 對它進行了一些操作,改變了它的這種狀態(tài)。例如,IsClosed 函數(shù)返回 true,但這時有另一個 goroutine 關(guān)閉了 channel,而你還拿著這個過時的 “channel 未關(guān)閉”的信息,向其發(fā)送數(shù)據(jù),就會導(dǎo)致 panic 的發(fā)生。當然,一個 channel 不會被重復(fù)關(guān)閉兩次,如果 IsClosed 函數(shù)返回的結(jié)果是 true,說明 channel 是真的關(guān)閉了。

有一條廣泛流傳的關(guān)閉 channel 的原則:

don’t close a channel from the receiver side and don’t close a channel if the channel has multiple concurrent senders.

不要從一個 receiver 側(cè)關(guān)閉 channel,也不要在有多個 sender 時,關(guān)閉 channel。

比較好理解,向 channel 發(fā)送元素的就是 sender,因此 sender 可以決定何時不發(fā)送數(shù)據(jù),并且關(guān)閉 channel。但是如果有多個 sender,某個 sender 同樣沒法確定其他 sender 的情況,這時也不能貿(mào)然關(guān)閉 channel。

但是上面所說的并不是最本質(zhì)的,最本質(zhì)的原則就只有一條:

don’t close (or send values to) closed channels.

有兩個不那么優(yōu)雅地關(guān)閉 channel 的方法:

1.使用 defer-recover 機制,放心大膽地關(guān)閉 channel 或者向 channel 發(fā)送數(shù)據(jù)。即使發(fā)生了 panic,有 defer-recover 在兜底。

2.使用 sync.Once 來保證只關(guān)閉一次。

那到底應(yīng)該如何優(yōu)雅地關(guān)閉 channel?

根據(jù) sender 和 receiver 的個數(shù),分下面幾種情況:

  • 一個 sender,一個 receiver
  • 一個 sender, M 個 receiver
  • N 個 sender,一個 reciver
  • N 個 sender, M 個 receiver

對于 1,2,只有一個 sender 的情況就不用說了,直接從 sender 端關(guān)閉就好了,沒有問題。重點關(guān)注第 3,4 種情況。

第 3 種情形下,優(yōu)雅關(guān)閉 channel 的方法是:the only receiver says “please stop sending more” by closing an additional signal channel。

解決方案就是增加一個傳遞關(guān)閉信號的 channel,receiver 通過信號 channel 下達關(guān)閉數(shù)據(jù) channel 指令。senders 監(jiān)聽到關(guān)閉信號后,停止發(fā)送數(shù)據(jù)。代碼如下:

func main() {
	rand.Seed(time.Now().UnixNano())
	const Max = 100000
	const NumSenders = 1000
	dataCh := make(chan int, 100)
	stopCh := make(chan struct{})
	// senders
	for i := 0; i < NumSenders; i++ {
		go func() {
			for {
				select {
				case <- stopCh:
					return
				case dataCh <- rand.Intn(Max):
				}
			}
		}()
	}
	// the receiver
	go func() {
		for value := range dataCh {
			if value == Max-1 {
				fmt.Println("send stop signal to senders.")
				close(stopCh)
				return
			}
			fmt.Println(value)
		}
	}()
	select {
	case <- time.After(time.Hour):
	}
}

這里的 stopCh 就是信號 channel,它本身只有一個 sender,因此可以直接關(guān)閉它。senders 收到了關(guān)閉信號后,select 分支 “case <- stopCh” 被選中,退出函數(shù),不再發(fā)送數(shù)據(jù)。

需要說明的是,上面的代碼并沒有明確關(guān)閉 dataCh。在 Go 語言中,對于一個 channel,如果最終沒有任何 goroutine 引用它,不管 channel 有沒有被關(guān)閉,最終都會被 gc 回收。所以,在這種情形下,所謂的優(yōu)雅地關(guān)閉 channel 就是不關(guān)閉 channel,讓 gc 代勞。

最后一種情況,優(yōu)雅關(guān)閉 channel 的方法是:any one of them says “let’s end the game” by notifying a moderator to close an additional signal channel。

和第 3 種情況不同,這里有 M 個 receiver,如果直接還是采取第 3 種解決方案,由 receiver 直接關(guān)閉 stopCh 的話,就會重復(fù)關(guān)閉一個 channel,導(dǎo)致 panic。因此需要增加一個中間人,M 個 receiver 都向它發(fā)送關(guān)閉 dataCh 的“請求”,中間人收到第一個請求后,就會直接下達關(guān)閉 dataCh 的指令(通過關(guān)閉 stopCh,這時就不會發(fā)生重復(fù)關(guān)閉的情況,因為 stopCh 的發(fā)送方只有中間人一個)。另外,這里的 N 個 sender 也可以向中間人發(fā)送關(guān)閉 dataCh 的請求。

func main() {
	rand.Seed(time.Now().UnixNano())
	const Max = 100000
	const NumReceivers = 10
	const NumSenders = 1000
	dataCh := make(chan int, 100)
	stopCh := make(chan struct{})
	// It must be a buffered channel.
	toStop := make(chan string, 1)
	var stoppedBy string
	// moderator
	go func() {
		stoppedBy = <-toStop
		close(stopCh)
	}()
	// senders
	for i := 0; i < NumSenders; i++ {
		go func(id string) {
			for {
				value := rand.Intn(Max)
				if value == 0 {
					select {
					case toStop <- "sender#" + id:
					default:
					}
					return
				}
				select {
				case <- stopCh:
					return
				case dataCh <- value:
				}
			}
		}(strconv.Itoa(i))
	}
	// receivers
	for i := 0; i < NumReceivers; i++ {
		go func(id string) {
			for {
				select {
				case <- stopCh:
					return
				case value := <-dataCh:
					if value == Max-1 {
						select {
						case toStop <- "receiver#" + id:
						default:
						}
						return
					}

					fmt.Println(value)
				}
			}
		}(strconv.Itoa(i))
	}
	select {
	case <- time.After(time.Hour):
	}
}

代碼里 toStop 就是中間人的角色,使用它來接收 senders 和 receivers 發(fā)送過來的關(guān)閉 dataCh 請求。

這里將 toStop 聲明成了一個 緩沖型的 channel。假設(shè) toStop 聲明的是一個非緩沖型的 channel,那么第一個發(fā)送的關(guān)閉 dataCh 請求可能會丟失。因為無論是 sender 還是 receiver 都是通過 select 語句來發(fā)送請求,如果中間人所在的 goroutine 沒有準備好,那 select 語句就不會選中,直接走 default 選項,什么也不做。這樣,第一個關(guān)閉 dataCh 的請求就會丟失。

如果,我們把 toStop 的容量聲明成 Num(senders) + Num(receivers),那發(fā)送 dataCh 請求的部分可以改成更簡潔的形式:

...
toStop := make(chan string, NumReceivers + NumSenders)
...
			value := rand.Intn(Max)
			if value == 0 {
				toStop <- "sender#" + id
				return
			}
...
				if value == Max-1 {
					toStop <- "receiver#" + id
					return
				}
...

直接向 toStop 發(fā)送請求,因為 toStop 容量足夠大,所以不用擔心阻塞,自然也就不用 select 語句再加一個 default case 來避免阻塞。

可以看到,這里同樣沒有真正關(guān)閉 dataCh,原樣同第 3 種情況。

到此這篇關(guān)于GoLang channel關(guān)閉狀態(tài)相關(guān)操作詳解的文章就介紹到這了,更多相關(guān)Go channel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang中實現(xiàn)類似類與繼承的方法(示例代碼)

    Golang中實現(xiàn)類似類與繼承的方法(示例代碼)

    這篇文章主要介紹了Golang中實現(xiàn)類似類與繼承的方法,Go語言中通過方法接受者的類型來決定方法的歸屬和繼承關(guān)系,本文通過示例代碼講解的非常詳細,需要的朋友可以參考下
    2024-04-04
  • Golang項目在github創(chuàng)建release后自動生成二進制文件的方法

    Golang項目在github創(chuàng)建release后自動生成二進制文件的方法

    這篇文章主要介紹了Golang項目在github創(chuàng)建release后如何自動生成二進制文件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • Golang 1.18新特性模糊測試用法詳解

    Golang 1.18新特性模糊測試用法詳解

    模糊測試是一種軟件測試技術(shù)。其核心思想是將自動或半自動生成的隨機數(shù)據(jù)輸入到一個程序中,并監(jiān)視程序異常,如崩潰,斷言失敗,以發(fā)現(xiàn)可能的程序錯誤,比如內(nèi)存泄漏,本文給大家介紹了Golang 1.18 新特性模糊測試,感興趣的同學可以參考閱讀下
    2023-05-05
  • Golang time包中的函數(shù)用法詳解

    Golang time包中的函數(shù)用法詳解

    在日常開發(fā)過程中,會頻繁遇到對時間進行操作的場景,使用 Golang 中的 time 包可以很方便地實現(xiàn)對時間的相關(guān)操作,接下來的幾篇文章會詳細講解 time 包,本文講解一下 time 包中的函數(shù),需要的朋友可以參考下
    2023-08-08
  • Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    在 Golang 中,經(jīng)常需要將其他類型(例如 slice、map、struct 等類型)的數(shù)據(jù)轉(zhuǎn)化為 JSON 格式,有時候轉(zhuǎn)化的結(jié)果并不是預(yù)期中的,例如將一個空的切片轉(zhuǎn)化為 JSON 時,會變成"null",所以本文將給大家介紹一下解決方法,需要的朋友可以參考下
    2023-09-09
  • GoLang編程必備:GoFrame?GoLand插件介紹

    GoLang編程必備:GoFrame?GoLand插件介紹

    掌握GoLang編程必備工具,我們來深入了解GoFrame?GoLand插件,這個小巧但強大的插件將極大提升你的開發(fā)效率,讓代碼流暢如行云流水,一起跟隨本指南,解鎖更智能的編程之旅!
    2023-12-12
  • Go語言接口的嵌套的具體使用

    Go語言接口的嵌套的具體使用

    在Go語言中,不僅結(jié)構(gòu)體與結(jié)構(gòu)體之間可以嵌套,接口與接口間也可以通過嵌套創(chuàng)造出新的接口,本文主要介紹了Go語言接口的嵌套的具體使用,感興趣的可以了解一下
    2023-04-04
  • Golang JSON的進階用法實例講解

    Golang JSON的進階用法實例講解

    這篇文章主要給大家介紹了關(guān)于Golang JSON進階用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用golang具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09
  • Go語言TCP從原理到代碼實現(xiàn)詳解

    Go語言TCP從原理到代碼實現(xiàn)詳解

    這篇文章主要為大家介紹了Go語言TCP從原理到代碼實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • go?sync?Waitgroup數(shù)據(jù)結(jié)構(gòu)實現(xiàn)基本操作詳解

    go?sync?Waitgroup數(shù)據(jù)結(jié)構(gòu)實現(xiàn)基本操作詳解

    這篇文章主要為大家介紹了go?sync?Waitgroup數(shù)據(jù)結(jié)構(gòu)實現(xiàn)基本操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01

最新評論

凤翔县| 厦门市| 毕节市| 葫芦岛市| 襄城县| 遂宁市| 鄂托克旗| 龙门县| 敖汉旗| 图木舒克市| 车险| 东城区| 土默特左旗| 长泰县| 平江县| 浪卡子县| 临泽县| 城固县| 札达县| 隆德县| 新邵县| 和政县| 香河县| 崇阳县| 桐乡市| 东方市| 孝昌县| 环江| 偏关县| 古丈县| 伊宁市| 铁岭县| 类乌齐县| 东丰县| 泰兴市| 湖北省| 康平县| 上思县| 渭南市| 沈阳市| 安义县|