<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一篇文章帶你了解Go語言基礎(chǔ)之并發(fā)(channel)

          共 503字,需瀏覽 2分鐘

           ·

          2020-12-29 18:39

          點(diǎn)擊上方“Go語言進(jìn)階學(xué)習(xí)”,進(jìn)行關(guān)注

          回復(fù)“Go語言”即可獲贈(zèng)從入門到進(jìn)階共10本電子書

          青山隱隱水迢迢, 秋盡江南草未凋。

          前言

          Hi,大家好,我是碼農(nóng),星期八,本篇繼續(xù)帶來Go語言并發(fā)基礎(chǔ),channel如何使用。

          看看Go協(xié)程如何配合channel。

          快來上車叭。

          為什么需要channel

          channel在Go中,也叫做管道,是用來多線程之間共享數(shù)據(jù)的。

          通常情況下,在Go中共享數(shù)據(jù)用的也是channel,但是在Go有兩種共享數(shù)據(jù)方式。

          • 共享內(nèi)存實(shí)現(xiàn)通訊。

          • 通過管道(channel)通訊(推薦)。

          為啥子共享內(nèi)存通訊不太推薦?

          示例代碼:多線程修改一個(gè)值。

          函數(shù)

          func Calc() {    defer wg.Done()    NUM = NUM - 1}

          main

          var NUM = 100var wg sync.WaitGroup
          func main() { for i := 0; i<100;i++ { wg.Add(1) go Calc()} wg.Wait() fmt.Println(NUM)}

          執(zhí)行結(jié)果

          沒錯(cuò),是2,懵了吧,哈哈哈,理論應(yīng)該是0才對(duì)呀。

          這是為啥?

          這就是共享內(nèi)存不太推薦的原因,我們的代碼已經(jīng)是多線程了。

          第一個(gè)函數(shù)代碼中,第3行,NUM = NUM - 1

          如果多個(gè)線程同時(shí)執(zhí)行到這一行,并且沒有加鎖,就會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂。

          那該怎么做呢?

          加鎖,加鎖可以保證某一段代碼只能被一個(gè)線程執(zhí)行,防止被爭搶。

          代碼

          func Calc() {    defer wg.Done()    mutex.Lock()    NUM = NUM - 1    mutex.Unlock()}

          第3行加鎖,第5行解鎖

          執(zhí)行結(jié)果

          這次真的是0的,不管執(zhí)行幾次。

          但是會(huì)發(fā)現(xiàn)一個(gè)問題,如果采用這種方式,需要常常注意競爭問題。

          所以不是太推薦,需要考慮的比較多,并且各種加鎖會(huì)消耗性能。

          channel語法

          channel格式

          var 變量名 chan 類型例如var x1 chan int //x1管道里面只能存int類型數(shù)據(jù)var x2 chan string //x2管道里面只能存字符串類型數(shù)據(jù)

          注意

          定義管道時(shí),chan int是一個(gè)整體,別搞錯(cuò)了各位。

          創(chuàng)建channel

          創(chuàng)建channel,只能通過make創(chuàng)建。

          格式

          var 變量名 = make(chan 類型,[管道大小])示例var chan1 = make(chan int10)//管道可以放10個(gè)int元素var chan2 = make(chan string5)//管道可以放5個(gè)string元素

          channel操作

          創(chuàng)建一個(gè)管道。

          ch = make(chan int10)

          channel是一個(gè)管道,就像一個(gè)管子。

          所以可以像管子里面塞東西,并且能取東西關(guān)閉管道就是這個(gè)管道不能用了,里面的值取完就打樣了

          像管子塞東西(發(fā)送)ch <- 666

          從管子取東西(接收)var x = <- ch

          關(guān)閉管子close(ch)

          注意:channel是先入先出結(jié)構(gòu),就像這樣。

          注意事項(xiàng):

          • 如果通道塞滿了,再塞 會(huì)阻塞住。

          • 如果通道關(guān)閉了,是不能再塞值了,否則會(huì)panic。

          • 即使通道關(guān)閉了,依然可以取值,直到將管道的值取完,取完后得到的是對(duì)應(yīng)類型零值。

          • 管道不能重復(fù)關(guān)閉,重復(fù)關(guān)閉會(huì)panic。


          無緩沖管道

          無緩沖就是這個(gè)管道沒有長度,就像這樣。

          就像快讀員沒有快遞柜,需要直接將快遞給客戶,如果沒人要就撂攤子。

          示例代碼

          package main
          import ( "fmt")
          //模擬張三func 張三(x chan string) { var a = <-x fmt.Println(a)}
          func main() { //通道沒有長度,就是無緩沖通道 var x = make(chan string) go 張三(x) x <- "張三的快遞" fmt.Println("張三快遞交付成功")}

          第16行寫入一個(gè)值,同理,張三就要等著去接,如果沒人接,那就完了。

          假設(shè)注釋第9行代碼。

          直接報(bào)錯(cuò),all goroutines are asleep - deadlock!,這句話的意思是所有的協(xié)程都睡著了,死鎖

          無緩沖說明通道長度為0發(fā)送一個(gè)值會(huì)阻塞住

          這就相當(dāng)于快遞員直接找張三,但是張三沒了,但是快遞員還得一直等著,等等等,然后掛了,終究還是沒送出去。

          有緩沖管道

          這個(gè)就簡單啦,多了一個(gè)快遞柜,快遞員直接將快遞仍快遞柜就行了。

          示例代碼

          package main
          import ( "fmt" "sync")
          var wg sync.WaitGroup
          //快遞員,快遞員放10個(gè)快遞func 快遞員(kuaidigui chan string) { defer wg.Done() for i := 0; i < 10; i++ { fmt.Println("快遞員放入了第",i,"快遞") kuaidigui <- fmt.Sprintf("第%d個(gè)快遞", i)} //放完快遞就關(guān)閉了通道 close(kuaidigui)}
          //張三,拿走3個(gè)快遞func 張三(kuaidigui chan string) { defer wg.Done() for i := 0; i < 3; i++ { fmt.Println("張三拿走" + <-kuaidigui)}}//李四拿走7個(gè)快遞func 李四(kuaidigui chan string) { defer wg.Done() for i := 0; i < 7; i++ { fmt.Println("李四拿走" + <-kuaidigui)}}func main() { //快遞柜,10個(gè)大小 var 快遞柜 = make(chan string10) wg.Add(3) go 快遞員(快遞柜) go 張三(快遞柜) go 李四(快遞柜) wg.Wait()}

          執(zhí)行結(jié)果

          遍歷channel兩種方式

          代碼

          func main() {    //快遞柜,10個(gè)大小    var ch = make(chan int10)    //向管道中發(fā)送值    for i := 0; i < 10; i++ {        ch <- i}    //方式一取值    //for {    //i, ok := <-ch    ////取完值ok就是false    //if !ok {    //      //結(jié)束循環(huán)    //      break    //}    //fmt.Println(i)    //}    //方式二取值    for i:=range ch{        fmt.Println(i)}}

          執(zhí)行結(jié)果

          報(bào)錯(cuò)是因?yàn)槲以趍ain中完成了發(fā)送值和取值兩個(gè)操作,所以會(huì)出現(xiàn)上述問題,但是結(jié)果是沒有錯(cuò)的。

          單向通道

          我們知道通道是可以發(fā)送值取值的,但是某些場景為了安全起見,理論來說只能取值,后者只能發(fā)送值。

          單向通道通常只在函數(shù)參數(shù)中體現(xiàn)。

          • 形參 chan<- chan類型只寫。

          • 形參 <-chan chan類型只讀。

          修改上述快遞員代碼。

          package main
          import ( "fmt" "sync")
          var wg sync.WaitGroup
          //快遞員,快遞員放10個(gè)快遞,只寫 chan<- stringfunc 快遞員(kuaidigui chan<- string) { defer wg.Done() for i := 0; i < 10; i++ { fmt.Println("快遞員放入了第", i, "快遞") kuaidigui <- fmt.Sprintf("第%d個(gè)快遞", i)} //放完快遞就關(guān)閉了通道 close(kuaidigui)}
          //張三,拿走3個(gè)快遞,只讀<-chan stringfunc 張三(kuaidigui <-chan string) { defer wg.Done() for i := 0; i < 3; i++ { fmt.Println("張三拿走" + <-kuaidigui)}}
          //李四拿走7個(gè)快遞func 李四(kuaidigui <-chan string) { defer wg.Done() for i := 0; i < 7; i++ { fmt.Println("李四拿走" + <-kuaidigui)}}func main() { //快遞柜,10個(gè)大小 var 快遞柜 = make(chan string10) wg.Add(3) go 快遞員(快遞柜) go 張三(快遞柜) go 李四(快遞柜) wg.Wait()}

          總結(jié)

          上述講述了Go語言并發(fā)如何和channel配合使用,畢竟我們一般的任務(wù)都不是單獨(dú)運(yùn)行的,都是互相配合的。

          我們講述了如何創(chuàng)建channel如何使用channel有緩沖管道和無緩沖管道區(qū)別,并且拒了一個(gè)快遞員例子來展示協(xié)程和channel如何配合,最后用單向通道又加固了一下代碼。

          我的代碼中使用了中文命名變量名是為了好看,實(shí)際開發(fā)中千萬不要這樣!!!

          上述代碼一定要敲一下,如果在操作過程中有任何問題,記得下面留言,我們看到會(huì)第一時(shí)間解決問題。

          不積跬步無以至千里,不積小流無以成江海,給自己一個(gè)成長的時(shí)間

          我是碼農(nóng)星期八,如果覺得還不錯(cuò),記得動(dòng)手點(diǎn)贊一下哈。

          感謝你的觀看。

          如果你覺得文章還可以,記得點(diǎn)贊留言支持我們哈。感謝你的閱讀,有問題請(qǐng)記得在下方留言噢~

          想了解更多關(guān)于Go的知識(shí),請(qǐng)前往:http://pdcfighting.com/

          -------------------?End?-------------------

          往期精彩文章推薦:

          歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持

          想加入Go學(xué)習(xí)群請(qǐng)?jiān)诤笈_(tái)回復(fù)【入群

          萬水千山總是情,點(diǎn)個(gè)【在看】行不行

          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉伊人在线视频 | 日美三级片 | 芲井空αv无码一区二区三区 | 欧美三级欧美一级 | 欧美成人精品欧美一级乱黄 |