国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Golang 并發(fā) Channel的用法

這篇具有很好參考價值的文章主要介紹了Golang 并發(fā) Channel的用法。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Golang 并發(fā) Channel的用法

channel 的創(chuàng)建

ch := make(chan int)

上面是創(chuàng)建了無緩沖的 channel,一旦有 goroutine 往 channel 發(fā)送數(shù)據(jù),那么當前的 goroutine 會被阻塞住,直到有其他的 goroutine 消費了 channel 里的數(shù)據(jù),才能繼續(xù)運行。

ch := make(chan int, 2)

上面示例中的第二個參數(shù)表示 channel 可緩沖數(shù)據(jù)的容量。只要當前 channel 里的元素總數(shù)不大于這個可緩沖容量,則當前的 goroutine 就不會被阻塞住。

nil channel

nil是pointers, interfaces, maps, slices, channels 和 function 類型的零值,表示未初始化值。nil不是未定義狀態(tài),它本身就是值。error是接口類型,因此error變量可以為nil,但string不能為nil。

下面我們看下nil 通道有什么特點,空通道對操作的反應如下:

  • 從空通道讀、寫會永遠阻塞
  • 關閉通道會終止程序(panic)

空通道是一種特殊通道,總是阻塞。對比非空已關閉的通道仍然可以進行讀取,并能夠讀取對應類型的零值,但對于已關閉的通道發(fā)送信息會終止程序。

一般 nil channel 用在 select 上,讓 select 不再從這個 channel 里讀取數(shù)據(jù)

讀寫阻塞示例

示例如下:

func TestNil(t *testing.T) {
	c := make(chan int)

	go sendIntegers(c)
	addIntegers(c)
}

func addIntegers(c chan int) {
	sum := 0
	t := time.NewTimer(time.Second * 5)
	for {
		select {
		case input := <-c:
			sum = sum + input
			fmt.Println("addIntegers , input : " + strconv.Itoa(input) + " , sum : " + strconv.Itoa(sum))
		case <-t.C:
			c = nil
			fmt.Println("addIntegers , nil channel , sum : " + strconv.Itoa(sum))
		}
	}
}

func sendIntegers(c chan int) {
	for {
		time.Sleep(time.Second * 1)
		c <- rand.Intn(100)
	}
}

輸出如下

=== RUN   TestNil
addIntegers , input : 81 , sum : 81
addIntegers , input : 87 , sum : 168
addIntegers , input : 47 , sum : 215
addIntegers , input : 59 , sum : 274
addIntegers , nil channel , sum : 274
panic: test timed out after 30s

此示例會一直阻塞下去,addIntegers是程序的主協(xié)程會一直阻塞下去,sendIntegers是子協(xié)程同樣會一直阻塞下去。

其中:輸出中的panic是單元測試的Test引發(fā)的異常,不需要考慮在內(nèi)。

close示例

func TestCloseNil(t *testing.T) {
	c := make(chan int)
	go writeChannel(c)
	num := <-c
	fmt.Println("main goroutine , read num : " + strconv.Itoa(num))
	c = nil
	fmt.Println("main goroutine , to close channel .")
	close(c)
	time.Sleep(time.Second * 10)

}

func writeChannel(c chan int) {
	fmt.Println("writeChannel goroutine ,  running ...")
	c <- 1
}

輸出如下

=== RUN   TestCloseNil
writeChannel goroutine ,  running ...
main goroutine , read num : 1
main goroutine , to close channel .
--- FAIL: TestCloseNil (0.00s)
panic: close of nil channel [recovered]
	panic: close of nil channel

關閉nil通道會引起程序panic

channel 的讀寫

寫操作

ch := make(chan int)
ch <- 1

讀操作

data <- ch

當我們不再使用 channel 的時候,可以對其進行關閉:

 close(ch)

不過讀取關閉后的 channel,不會產(chǎn)生 pannic,還是可以讀到數(shù)據(jù)。

如果關閉后的 channel 沒有數(shù)據(jù)可讀取時,將得到零值,即對應類型的默認值。

為了能知道當前 channel 是否被關閉,可以使用下面的寫法來判斷。

 if v, ok := <-ch; !ok {
  fmt.Println("channel 已關閉,讀取不到數(shù)據(jù)")
 }

還可以使用下面的寫法不斷的獲取 channel 里的數(shù)據(jù):

 for data := range ch {
  // get data dosomething
 }

這種用法會在讀取完 channel 里的數(shù)據(jù)后就結束 for 循環(huán),執(zhí)行后面的代碼。

channel 只讀只寫

在默認情況下,管道是雙向的,可讀可寫,在使用 channel 時我們還可以控制 channel 只讀只寫操作:

聲明為只寫,如下:

var chan2 chan<- int
chan2 = make(chan int, 3)
chan2 <- 20

如果試著讀此chan,則編譯報錯,編譯錯誤如下:

invalid operation: cannot receive from send-only channel chan2 (variable of type chan<- int) compiler (InvalidReceive)

聲明為只讀,不可寫,否則編譯報錯,如下:

var chan3 <-chan int
nm2 := <-chan3

函數(shù)可以聲明chan只讀只寫,代碼示例:

// 只寫操作
func send(ch chan<- int, exitChan chan struct{}) {
	for i := 0; i < 5; i++ {
		time.Sleep(time.Second * 1)
		ch <- i
	}
	close(ch)
	var a struct{}
	exitChan <- a
}

// 只讀操作
func recv(ch <-chan int, exitChan chan struct{}) {
	for {
		v, ok := <-ch
		if !ok {
			break
		}
		fmt.Println("recv goroutine , value : " + strconv.Itoa(v))
	}
	var a struct{}
	exitChan <- a
}
func TestOnlyReadWrite(t *testing.T) {
	ch := make(chan int, 10)
	exitChan := make(chan struct{}, 2)
	go send(ch, exitChan)
	go recv(ch, exitChan)
	var total = 0
	for _ = range exitChan {
		total++
		if total == 2 {
			break
		}
	}
	fmt.Println("main goroutine , 結束")
}

輸出如下:

=== RUN   TestOnlyReadWrite
recv goroutine , value : 0
recv goroutine , value : 1
recv goroutine , value : 2
recv goroutine , value : 3
recv goroutine , value : 4
main goroutine , 結束
--- PASS: TestOnlyReadWrite (5.03s)

關閉channel

channel關閉后,剩余的數(shù)據(jù)能否取到

golang channel關閉后,其中剩余的數(shù)據(jù),是可以繼續(xù)讀取的,channel關閉之后,仍然可以從channel中讀取剩余的數(shù)據(jù),直到數(shù)據(jù)全部讀取完成。

對于關閉的channel的讀寫需要注意兩點:

  • 如果繼續(xù)向channel發(fā)送數(shù)據(jù),會引起panic,
  • 如果繼續(xù)讀數(shù)據(jù),得到的是零值(對于int,就是0)。

讀取關閉的channel,將獲取零值

當讀取已關閉的channel時,如果繼續(xù)讀取channel,獲取到的是零值,不會堵塞,

另外即使是無緩沖的channel,也將能一直獲取到零值。

代碼示例如下

func TestCloseDemo01(t *testing.T) {

	done := make(chan struct{})
	ch := make(chan int, 3)
	ch <- 1
	ch <- 2
	ch <- 3
	close(ch)

	go func() {
		for {
			value := <-ch
			//此處為假設判斷,value永遠不會等于10
			if value == 10 {
				break
			}
			fmt.Println("read channel , value : ", value)
			time.Sleep(time.Second * 1)
		}
		done <- struct{}{}
	}()

	select {
	case <-done:
		fmt.Println("讀取channel,正常結束")
	case <-time.After(time.Second * 5):
		fmt.Println("超時退出")
	}
}

輸出如下:

=== RUN   TestCloseDemo01
read channel , value :  1
read channel , value :  2
read channel , value :  3
read channel , value :  0
read channel , value :  0
超時退出
--- PASS: TestCloseDemo01 (5.00s)

使用ok判斷,是否關閉

讀取channel,判斷是否關閉:

value, ok := <-ch
  • 當channel關閉時,ok=false
  • 當channel未關閉時,ok=true

通過判斷channel是否關閉,當channel關閉時,程序可以正常退出,代碼示例如下:

func TestCloseDemo02(t *testing.T) {

	done := make(chan struct{})
	ch := make(chan int, 3)
	ch <- 1
	ch <- 2
	ch <- 3
	close(ch)

	go func() {
		for {
			value, ok := <-ch
			if !ok {
				break
			}
			fmt.Println("read channel , value : ", value)
			time.Sleep(time.Second * 1)
		}
		done <- struct{}{}
	}()

	select {
	case <-done:
		fmt.Println("讀取channel,正常結束")
	case <-time.After(time.Second * 5):
		fmt.Println("超時退出")
	}
}

輸出如下:

=== RUN   TestCloseDemo02
read channel , value :  1
read channel , value :  2
read channel , value :  3
讀取channel,正常結束
--- PASS: TestCloseDemo02 (3.03s)
PASS

使用for-range退出

for-range是使用頻率很高的結構,常用它來遍歷數(shù)據(jù),range能夠感知channel的關閉,當channel被發(fā)送數(shù)據(jù)的協(xié)程關閉時,range就會結束,接著退出for循環(huán)。

它在并發(fā)中的使用場景是:當協(xié)程只從1個channel讀取數(shù)據(jù),然后進行處理,處理后協(xié)程退出。

下面這個示例程序,當通道被關閉時,協(xié)程可自動退出。

func TestCloseDemo02(t *testing.T) {
	ch := make(chan int, 3)
	ch <- 1
	ch <- 2
	ch <- 3
	close(ch)
	for v := range ch {
		fmt.Println("value", v)
	}
	time.Sleep(time.Second * 10)
}

使用close(ch)關閉所有下游協(xié)程

  • 關閉通道,可以主動通知所有協(xié)程退出的場景

當啟動100個worker時,只要main()執(zhí)行關閉stopCh,每一個worker都會都到信號,進而關閉。如果main()向stopCh發(fā)送100個數(shù)據(jù),這種就低效了。

//close關閉所有子協(xié)程
func TestCloseDemo04(t *testing.T) {

	ch := make(chan int, 3)
	stopCh := make(chan struct{})

	for i := 1; i < 6; i++ {
		worker("worker"+strconv.Itoa(i), stopCh, ch)
	}

	time.Sleep(time.Second * 5)
	close(stopCh)

	time.Sleep(time.Second * 5)
}


func worker(workerName string, stopCh <-chan struct{}, ch <-chan int) {
	go func() {
		defer fmt.Println(workerName, "goroutine , worker exit")
		// Using stop channel explicit exit
		for {
			select {
			case <-stopCh:
				fmt.Println(workerName, "goroutine , Recv stop signal , return")
				return
			default:
				fmt.Println(workerName, "goroutine , worker default ...")
			}
			time.Sleep(time.Second * 3)
		}
	}()
}

輸出如下

=== RUN   TestCloseDemo04
worker5 goroutine , worker default ...
worker3 goroutine , worker default ...
worker4 goroutine , worker default ...
worker1 goroutine , worker default ...
worker2 goroutine , worker default ...
worker3 goroutine , worker default ...
worker2 goroutine , worker default ...
worker5 goroutine , worker default ...
worker4 goroutine , worker default ...
worker1 goroutine , worker default ...
worker4 goroutine , Recv stop signal , return
worker4 goroutine , worker exit
worker2 goroutine , Recv stop signal , return
worker2 goroutine , worker exit
worker5 goroutine , Recv stop signal , return
worker5 goroutine , worker exit
worker1 goroutine , Recv stop signal , return
worker1 goroutine , worker exit
worker3 goroutine , Recv stop signal , return
worker3 goroutine , worker exit
--- PASS: TestCloseDemo04 (10.01s)
PASS

函數(shù)傳遞引用or值

golang 傳遞給函數(shù)chan類型時,是值傳遞和引用傳遞?

  • golang默認都是采用值傳遞,即拷貝傳遞
  • 有些值天生就是指針(slice、map、channel)

可以看出來map和slice都是指針傳遞,即函數(shù)內(nèi)部是可以改變參數(shù)的值的。而array是數(shù)組傳遞,不管函數(shù)內(nèi)部如何改變參數(shù),都是改變的拷貝值,并未對原值進行處理。

在 Go 語言中,所有的函數(shù)參數(shù)傳遞都是值傳遞(pass by value),當將參數(shù)傳遞給函數(shù)時,實際上是將參數(shù)的副本傳遞給函數(shù)。然而,這并不意味著在函數(shù)內(nèi)部對參數(shù)的修改都不會影響原始數(shù)據(jù)。因為在 Go 中,有些數(shù)據(jù)類型本身就是引用類型,比如切片(slice)、映射(map)、通道(channel)、接口(interface)和指針(pointer)。當這些類型作為參數(shù)傳遞給函數(shù)時,雖然傳遞的是值,但值本身就是一個引用。

小結
Go 語言中的參數(shù)傳遞總是值傳遞,意味著傳遞的總是變量的副本,無論是基本數(shù)據(jù)類型還是復合數(shù)據(jù)類型。由于復合數(shù)據(jù)類型(如切片、映射、通道、接口和指針)內(nèi)部包含的是對數(shù)據(jù)的引用,所以在函數(shù)內(nèi)部對這些參數(shù)的修改可能會影響到原始數(shù)據(jù)。理解這一點對于編寫正確和高效的Go代碼至關重要。

另外即使是引用類型,比如切片,當長度或容量(比如使用 append 函數(shù))發(fā)生變化了,可能會導致分配新的底層數(shù)組。這種情況下,原始切片不會指向新的數(shù)組,但是函數(shù)內(nèi)部的切片會。因此,如果想在函數(shù)內(nèi)部修改切片的長度或容量并反映到外部,應該傳遞一個指向切片的指針。文章來源地址http://www.zghlxwxcb.cn/news/detail-832661.html

參考

  • https://www.cnblogs.com/-wenli/p/12350181.html
  • https://segmentfault.com/a/1190000017958702
  • https://zhuanlan.zhihu.com/p/395278270
  • https://zhuanlan.zhihu.com/p/613771870
  • Go里面如何實現(xiàn)廣播 https://juejin.cn/post/6844903857395335182

到了這里,關于Golang 并發(fā) Channel的用法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • golang發(fā)送get請求的各種操作:自定義header請求頭、帶cookie請求、channel并發(fā)請求

    請求參數(shù)放到url.Values{} 接收文件–ioutil.WriteFile 接收文件–io.Copy

    2024年02月06日
    瀏覽(21)
  • golang實現(xiàn)webgis后端開發(fā)

    golang實現(xiàn)webgis后端開發(fā)

    目錄 前言 二、實現(xiàn)步驟 1.postgis數(shù)據(jù)庫和model的綁定 2.將pg庫中的要素轉(zhuǎn)換為geojson (1)幾何定義 (2)將wkb解析為幾何類型 (3)定義geojson類型 (4)數(shù)據(jù)轉(zhuǎn)換 (5)數(shù)據(jù)返回 ?2.前端傳入的geojson儲存到數(shù)據(jù)庫 3、其他功能實現(xiàn) 總結 ????????停更了接近一個月都在研究一門新語言gola

    2024年02月08日
    瀏覽(26)
  • golang學習-channel管道

    1、定義 管道是golang語言提供的goroutine間的通訊方式,channel可以讓一個goroutine發(fā)送特定的值給另一個goroutine的通訊機制。 管道是引用類型。 golang語言中channel是一種特殊的類型。像一個隊列一樣,先進先出。 var 變量 chan 元素類型 var ch1 chan int //聲明一個傳遞整型的管道 var

    2024年01月19日
    瀏覽(24)
  • golang channel

    channel是不同協(xié)程之間異步通信的數(shù)據(jù)結構。 1 構造 2 讀操作 3 寫 4 關閉 5 多路復用 實現(xiàn)對多個channel同時監(jiān)聽 三個核心: 1 并發(fā)讀寫安全,需要鎖 2 環(huán)形緩沖區(qū)(數(shù)組+頭尾指針),好處是復用數(shù)組空間,同時保證內(nèi)存地址連續(xù) 3 承載阻塞goroutine的隊列 hchan:channel數(shù)據(jù)結構

    2024年02月11日
    瀏覽(14)
  • Golang之Channel詳細介紹

    一、概述 通道(Channel)是 Golang 在語言級別上提供的 goroutine 間的通訊方式,可以使用channel在多個 goroutine 之間傳遞消息。如果說 goroutine 是 Go 程序并發(fā)的執(zhí)行體,channel 就是它們之間的連接。channel 是可以讓一個 goroutine 發(fā)送特定值到另一個 goroutine 的通信機制。 Golang 的并發(fā)

    2024年02月12日
    瀏覽(24)
  • golang channel執(zhí)行原理與代碼分析

    golang channel執(zhí)行原理與代碼分析

    使用的go版本為 go1.21.2 首先我們寫一個簡單的chan調(diào)度代碼 因為ch的數(shù)據(jù)獲取方式有兩種,所以這個示例代碼寫了兩次的ch讀與寫 老樣子通過go build -gcflags -S main.go獲取到對應的匯編代碼 調(diào)度make最終被轉(zhuǎn)換為CALL runtime.makechan 調(diào)度ch - struct{}{}最終被轉(zhuǎn)換為CALL runtime.chansend1 由于我

    2024年02月05日
    瀏覽(17)
  • 001 Golang-channel-practice

    001 Golang-channel-practice

    最近在練習并發(fā)編程。加上最近也在用Golang寫代碼,所以記錄一下練習的題目。 ? 第一道題目是用10個協(xié)程打印100條信息,創(chuàng)建10個協(xié)程。每個協(xié)程都會有自己的編號。每個協(xié)程都會被打印10次。 ? 本題的關鍵是: 創(chuàng)建goroutines :通過 for i := 0; i 10; i++ 循環(huán),創(chuàng)建了10個gorout

    2024年02月02日
    瀏覽(23)
  • 詳解如何在Golang中監(jiān)聽多個channel

    這篇文章主要為大家詳細介紹了如何在Golang中實現(xiàn)監(jiān)聽多個channel,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下? ? 我們可以使用select來同時監(jiān)聽多個goroutine。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package main import ( ??

    2024年02月13日
    瀏覽(20)
  • 004 Golang-channel-practice 左右括號匹配

    004 Golang-channel-practice 左右括號匹配

    第四題 左右括號打印 一個協(xié)程負責打印“(”,一個協(xié)程負責打印“)”,左右括號的數(shù)量要匹配。在這道題目里,我在main函數(shù)里進行了一個死循環(huán)。會產(chǎn)生一個隨機數(shù),隨機數(shù)就是接下來要打印的左括號的數(shù)量。 例如:((((()))))、(())、()。這樣是正確的。一個左括號要匹

    2024年02月02日
    瀏覽(67)
  • 007 Golang-channel-practice 打印水分子

    007 Golang-channel-practice 打印水分子

    對應leetcode 1117 https://leetcode.cn/problems/building-h2o/description/ 題目大意:在三個為一組的字符串中,打印兩個H,一個O,順序不限。 這在go里面很容易實現(xiàn)。只需要在每個函數(shù)前加上一個go,就可以輕松實現(xiàn)并發(fā)了。直接看代碼! 打印效果: ?

    2024年01月20日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包