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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Go基础--goroutine和channel

發(fā)布時間:2025/3/19 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go基础--goroutine和channel 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

goroutine

在go語言中,每一個并發(fā)的執(zhí)行單元叫做一個goroutine

這里說到并發(fā),所以先解釋一下并發(fā)和并行的概念:

并發(fā):邏輯上具備同時處理多個任務(wù)的能力

并行:物理上在同一時刻執(zhí)行多個并發(fā)任務(wù)

當(dāng)一個程序啟動時,其主函數(shù)即在一個單獨的goroutine中運行,一般這個goroutine是主goroutine

如果想要創(chuàng)建新的goroutine,只需要再執(zhí)行普通函數(shù)或者方法的的前面加上關(guān)鍵字go

通過下面一個例子演示并發(fā)的效果,主goroutine會計算第45個斐波那契函數(shù),在計算的同時會循環(huán)打印:-\|/??

這里需要知道:當(dāng)主goroutine結(jié)束之后,所有的goroutine都會被打斷,程序就會退出

package mainimport ("time""fmt" )func spinner(delay time.Duration){for {for _,r := range `-\|/`{fmt.Printf("\r%c",r)time.Sleep(delay)}} }func fib(x int) int{// 斐波那契函數(shù)if x < 2{return x}return fib(x-1) + fib(x-2) }func main() {go spinner(100*time.Millisecond) //開啟一個goroutineconst n = 45fibN:= fib(n)fmt.Printf("\rFib(%d) = %d\n",n,fibN) }

當(dāng)?shù)谝淮慰吹絞o的并發(fā),感覺真是太好用了!!!!

所以在網(wǎng)絡(luò)編程里,服務(wù)端都是需要同時可以處理很多個連接,我們看一下下面的服務(wù)端和客戶端例子

服務(wù)端:

package mainimport ("net""io""time""log" )func handleConn(c net.Conn){defer c.Close()for{_,err := io.WriteString(c,time.Now().Format("15:04:05\r\n"))if err != nil{return}time.Sleep(1*time.Second)} }func main() {// 監(jiān)聽本地tcp的8000端口listener,err := net.Listen("tcp","localhost:8000")if err != nil{log.Fatal(err)}for {conn,err := listener.Accept()if err!= nil{log.Print(err)continue}go handleConn(conn)} }

客戶端:

package mainimport ("io""log""net""os" )func mustCopy(dst io.Writer,src io.Reader){// 從連接中讀取內(nèi)容,并寫到標(biāo)準(zhǔn)輸出if _,err := io.Copy(dst,src);err !=nil{log.Fatal(err)}}func main(){conn,err := net.Dial("tcp","localhost:8000")if err != nil{log.Fatal(err)}defer conn.Close()mustCopy(os.Stdout, conn) }

Channel

channel是不同的goroutine之間的通信機制。

一個goroutine通過channel給另外一個goroutine發(fā)送信息。

每個channel 都有一個特殊的類型,也就是channel可以發(fā)送的數(shù)據(jù)的類型

我們可以通過make創(chuàng)建一個channel如:

ch := make(chan int)? 這就是創(chuàng)建了一個類型為int的channel

默認(rèn)我們這樣創(chuàng)建的是無緩存的channel,當(dāng)然我們可以通過第二個參數(shù)來設(shè)置容量

ch := make(chan int,10)

注意:channel是引用類型,channel的零值也是nil

兩個相同類型的channel可以使用==運算符比較。如果兩個channel引用的是相通的對象,那么比較的結(jié)
果為真。一個channel也可以和nil進(jìn)行比較。

因為channel是在不同的goroutine之間進(jìn)行通信的,所以channel這里有兩種操作:存數(shù)據(jù)和取數(shù)據(jù),而這里兩種操作的

方法都是通過運算符:<-

ch <- x? 這里是發(fā)送一個值x到channel中

x = <- ch 這里是從channel獲取一個值存到變量x

<-ch 這里是從channel中取出數(shù)據(jù),但是不使用結(jié)果

close(ch) 用于關(guān)閉channel

當(dāng)我們關(guān)閉channel后,再次發(fā)送就會導(dǎo)致panic異常,但是如果之前發(fā)送過數(shù)據(jù),我們在關(guān)閉channel之后依然可以執(zhí)行接收操作

如果沒有數(shù)據(jù)的話,會產(chǎn)生一個零值

?

基于channel發(fā)送消息有兩個重要方面,首先每個消息都有一個值,但是有時候通訊的事件和發(fā)送的時刻也同樣重要。

我們更希望強調(diào)通訊發(fā)送的時刻時,我們將它稱為消息事件。有些消息并不攜帶額外的信息,它僅僅是用做兩個goroutine之間的同步,這個時候我們可以用struct{}空結(jié)構(gòu)體作為channel元素的類型

?

?

?

?無緩存的channel

基于無緩存的channel的發(fā)送和接受操作將導(dǎo)致兩個goroutine做一次同步操作,所以無緩存channel有時候也被稱為同步channel

串聯(lián)的channel (Pipeline)

channel也可以用于多個goroutine連接在一起,一個channel的輸出作為下一個channel的輸入,這種串聯(lián)的channel就是所謂的pipeline

通過下面例子理解,第一個goroutine是一個計算器,用于生成0,1,2...形式的整數(shù)序列,然后通過channel將該整數(shù)序列

發(fā)送給第二個goroutine;第二個goroutine是一個求平方的程序,對收到的每個整數(shù)求平方,然后將平方后的結(jié)果通過第二個channel發(fā)送給第三個goroutine

第三個goroutine是一個打印程序,打印收到的每個整數(shù)

package mainimport ("fmt" )func main(){naturals := make(chan int)squares := make(chan int)go func(){for x:=0;;x++{naturals <- x}}()go func(){for {x := <- naturalssquares <- x * x}}()for{fmt.Println(<-squares)} }

但是如果我把第一個生成數(shù)的寫成一個有范圍的循環(huán),這個時候程序其實會報錯的

所以就需要想辦法讓發(fā)送知道沒有可以發(fā)給channel的數(shù)據(jù)了,也讓接受者知道沒有可以接受的數(shù)據(jù)了

這個時候就需要用到close(chan)

當(dāng)一個channel被關(guān)閉后,再向該channel發(fā)送數(shù)據(jù)就會導(dǎo)致panic異常

當(dāng)從一個已經(jīng)關(guān)閉的channel中接受數(shù)據(jù),在接收完之前發(fā)送的數(shù)據(jù)后,并不會阻塞,而會立刻返回零值,所以在從channel里接受數(shù)據(jù)的時候可以多獲取一個值如:

go func(){
for {
x ,ok := <-naturals
if !ok{
break
}
squares <- x*x
}
close(squares)
}()

第二位ok是一個布爾值,true表示成功從channel接受到值,false表示channel已經(jīng)被關(guān)閉并且里面沒有值可以接收

單方向的channel

當(dāng)一個channel座位一個函數(shù)的參數(shù)時,它一般總是被專門用于只發(fā)送或者只接收

chan <- int :表示一個只發(fā)送int的channel,只能發(fā)送不能接收

< chan int : 表示一個只接受int的channel,只能接收不能發(fā)送

?

當(dāng)然在有時候我們需要獲取channel內(nèi)部緩存的容量,可以通過內(nèi)置的cap函數(shù)獲取

而len函數(shù)則返回的是channel內(nèi)實際有效的元素個數(shù)

基于select的多路復(fù)用

?這里先說一個擁有的知識點:time.Tick函數(shù)

這個函數(shù)返回一個channel,通過下面代碼進(jìn)行理解:

package mainimport ("time""fmt" )func main() {tick := time.Tick(1*time.Second)for countdown :=10;countdown>0;countdown--{j :=<- tickfmt.Println(j)} }

程序會循環(huán)打印一個時間戳

?

select 語句:

select {case <-ch1:// ...case x := <-ch2:// ...use x...case ch3 <- y:// ...default: // ...
}

select語句的形式其實和switch語句有點類似,這里每個case代表一個通信操作

在某個channel上發(fā)送或者接收,并且會包含一些語句組成的一個語句塊 。

select中的default來設(shè)置當(dāng) 其它的操作都不能夠馬上被處理時程序需要執(zhí)行哪些邏輯

channel 的零值是nil,? 并且對nil的channel 發(fā)送或者接收操作都會永遠(yuǎn)阻塞,在select語句中操作nil的channel永遠(yuǎn)都不會被select到。

這可以讓我們用nil來激活或者禁用case,來達(dá)成處理其他輸出或者輸出時間超時和取消的邏輯

?

所有的努力都值得期許,每一份夢想都應(yīng)該灌溉!

總結(jié)

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

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