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

Go中Channel發(fā)送和接收操作指南

 更新時(shí)間:2021年08月24日 09:54:57   作者:呦呦鹿鳴  
在golang中channel屬于較為核心的一個(gè)功能,尤其在go協(xié)程中,channel功能尤為重要,下面這篇文章主要給大家介紹了關(guān)于Go中Channel發(fā)送和接收操作的相關(guān)資料,需要的朋友可以參考下

前言

先來看一道面試題:

對(duì)已經(jīng)關(guān)閉的 chan 進(jìn)行讀寫,會(huì)怎么樣?為什么?

在上一篇學(xué)習(xí) Go 協(xié)程的文章中,知道 go 關(guān)鍵字可以用來開啟一個(gè) goroutine 進(jìn)行任務(wù)處理,但多個(gè)任務(wù)之間如果需要通信,就需要用到通道(channel)了。

一、Channel的定義

聲明并初始化一個(gè)通道,可以使用 Go 語言的內(nèi)建函數(shù) make,同時(shí)指定該通道類型的元素類型,下面聲明了一個(gè) chan int 類型的 channel:

ch := make(chan int)

二、Channel的操作

發(fā)送(寫):發(fā)送操作包括了“復(fù)制元素值”和“放置副本到通道內(nèi)部”這兩個(gè)步驟。即:進(jìn)入通道的并不是操作符右邊的那個(gè)元素值,而是它的副本。

ch := make(chan int)

// write to channel
ch <- x

接收(讀):接收操作包含了“復(fù)制通道內(nèi)的元素值”、“放置副本到接收方”、“刪掉原值”三個(gè)步驟。

ch := make(chan int)

// read from channel
x <- ch

// another way to read
x = <- ch

關(guān)閉:關(guān)閉 channel 會(huì)產(chǎn)生一個(gè)廣播機(jī)制,所有向 channel 讀取消息的 goroutine 都會(huì)收到消息。

ch := make(chan int)

close(ch)

從一個(gè)已關(guān)閉的 channel 中讀取消息永遠(yuǎn)不會(huì)阻塞,并且會(huì)返回一個(gè)為 false 的 ok-idiom,可以用它來判斷 channel 是否關(guān)閉:

v, ok := <-ch

如果 ok 是false,表明接收的 v 是產(chǎn)生的零值,這個(gè) channel 被關(guān)閉了或者為空。

三、Channel發(fā)送和接收操作的特點(diǎn)

  1. 一個(gè)通道相當(dāng)于一個(gè)先進(jìn)先出(FIFO)的隊(duì)列:也就是說,通道中的各個(gè)元素值都是嚴(yán)格地按照發(fā)送的順序排列的,先被發(fā)送通道的元素值一定會(huì)先被接收。
  2. 對(duì)于同一個(gè)通道,發(fā)送操作之間和接收操作之間是互斥的:同一時(shí)刻,對(duì)同一通道發(fā)送多個(gè)元素,直到這個(gè)元素值被完全復(fù)制進(jìn)該通道之后,其他針對(duì)該通道的發(fā)送操作才可能被執(zhí)行。接收也是如此。
  3. 發(fā)送操作和接收操作中,對(duì)元素值的處理是不可分割的:前面我們知道發(fā)送一個(gè)值到通道,是先復(fù)制值,再將該副本移動(dòng)到通道內(nèi)部,“不可分割”指的是發(fā)送操作要么還沒復(fù)制元素值,要么已經(jīng)復(fù)制完畢,絕不會(huì)出現(xiàn)只復(fù)制了一部分的情況。接收也是同理,在準(zhǔn)備好元素值的副本之后,一定會(huì)刪除掉通道中的原值,絕不會(huì)出現(xiàn)通道中仍有殘留的情況。
  4. 發(fā)送操作和接收操作在完全完成之前會(huì)被阻塞:發(fā)送操作包括了“復(fù)制元素值”和“放置副本到通道內(nèi)部”這兩個(gè)步驟。在這兩個(gè)步驟完全完成之前,發(fā)起這個(gè)發(fā)送操作的那句代碼會(huì)一直阻塞在那里,在它之后的代碼不會(huì)有執(zhí)行的機(jī)會(huì),直到阻塞解除。

四、Channel的類型

channel 分為不帶緩存的 channel 和帶緩存的 channel。

使用 make 聲明一個(gè)通道類型變量時(shí),除了指定通道的元素類型,還可以指定通道的容量,也就是通道最多可以緩存多少個(gè)元素值,當(dāng)容量為 0 時(shí),該通道為非緩沖通道,當(dāng)容量大于 0 時(shí),該通道為帶有緩沖的通道。

ch := make(chan int)    //無緩沖的channel
ch := make(chan int, 3) //帶緩沖的channel

非緩沖通道和緩沖通道有著不同的數(shù)據(jù)傳遞方式:

  • 非緩沖通道:無論是發(fā)送操作還是接收操作,一開始執(zhí)行就會(huì)被阻塞,直到配對(duì)的操作也開始執(zhí)行,才會(huì)繼續(xù)傳遞。即:只有收發(fā)雙方對(duì)接上了,數(shù)據(jù)才會(huì)被傳遞。數(shù)據(jù)直接從發(fā)送方復(fù)制到接收方。非緩沖通道傳遞數(shù)據(jù)的方式是同步的。
  • 緩沖通道:如果通道已滿,對(duì)它的所有發(fā)送操作都會(huì)被阻塞,直到通道中有元素值被接收走。反之,如果通道已空,那么對(duì)它的所有接收操作都會(huì)被阻塞,直到通道中有新的元素值出現(xiàn)。元素值會(huì)先從發(fā)送方復(fù)制到緩沖通道,之后再由緩沖通道復(fù)制給接收方。緩沖通道傳遞數(shù)據(jù)的方式是異步的。

五、Channel的源碼學(xué)習(xí)

Channel 的主要實(shí)現(xiàn)在 src/runtime/chan.go 中,go 版本為 go version go1.14.6 darwin/amd64這里主要看 chansend 如何實(shí)現(xiàn)的。

func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
 if c == nil {
  if !block {
   return false
  }
  gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)
  throw("unreachable")
 }

 if debugChan {
  print("chansend: chan=", c, "\n")
 }

 if raceenabled {
  racereadpc(c.raceaddr(), callerpc, funcPC(chansend))
 }

 // Fast path: check for failed non-blocking operation without acquiring the lock.
 //
 // After observing that the channel is not closed, we observe that the channel is
 // not ready for sending. Each of these observations is a single word-sized read
 // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).
 // Because a closed channel cannot transition from 'ready for sending' to
 // 'not ready for sending', even if the channel is closed between the two observations,
 // they imply a moment between the two when the channel was both not yet closed
 // and not ready for sending. We behave as if we observed the channel at that moment,
 // and report that the send cannot proceed.
 //
 // It is okay if the reads are reordered here: if we observe that the channel is not
 // ready for sending and then observe that it is not closed, that implies that the
 // channel wasn't closed during the first observation.
 if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
  (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
  return false
 }

 var t0 int64
 if blockprofilerate > 0 {
  t0 = cputicks()
 }

 lock(&c.lock)

 if c.closed != 0 {
  unlock(&c.lock)
  panic(plainError("send on closed channel"))
 }

 if sg := c.recvq.dequeue(); sg != nil {
  // Found a waiting receiver. We pass the value we want to send
  // directly to the receiver, bypassing the channel buffer (if any).
  send(c, sg, ep, func() { unlock(&c.lock) }, 3)
  return true
 }

 if c.qcount < c.dataqsiz {
  // Space is available in the channel buffer. Enqueue the element to send.
  qp := chanbuf(c, c.sendx)
  if raceenabled {
   raceacquire(qp)
   racerelease(qp)
  }
  typedmemmove(c.elemtype, qp, ep)
  c.sendx++
  if c.sendx == c.dataqsiz {
   c.sendx = 0
  }
  c.qcount++
  unlock(&c.lock)
  return true
 }

 if !block {
  unlock(&c.lock)
  return false
 }

 // Block on the channel. Some receiver will complete our operation for us.
 gp := getg()
 mysg := acquireSudog()
 mysg.releasetime = 0
 if t0 != 0 {
  mysg.releasetime = -1
 }
 // No stack splits between assigning elem and enqueuing mysg
 // on gp.waiting where copystack can find it.
 mysg.elem = ep
 mysg.waitlink = nil
 mysg.g = gp
 mysg.isSelect = false
 mysg.c = c
 gp.waiting = mysg
 gp.param = nil
 c.sendq.enqueue(mysg)
 gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2)
 // Ensure the value being sent is kept alive until the
 // receiver copies it out. The sudog has a pointer to the
 // stack object, but sudogs aren't considered as roots of the
 // stack tracer.
 KeepAlive(ep)

 // someone woke us up.
 if mysg != gp.waiting {
  throw("G waiting list is corrupted")
 }
 gp.waiting = nil
 gp.activeStackChans = false
 if gp.param == nil {
  if c.closed == 0 {
   throw("chansend: spurious wakeup")
  }
  panic(plainError("send on closed channel"))
 }
 gp.param = nil
 if mysg.releasetime > 0 {
  blockevent(mysg.releasetime-t0, 2)
 }
 mysg.c = nil
 releaseSudog(mysg)
 return true
}

從代碼中可以看到:

  • 有 goroutine 阻塞在 channel recv 隊(duì)列上,此時(shí)緩存隊(duì)列為空,直接將消息發(fā)送給 reciever goroutine,只產(chǎn)生一次復(fù)制。
  • 當(dāng) channel 緩存隊(duì)列有剩余空間時(shí),將數(shù)據(jù)放到隊(duì)列里,等待接收,接收后總共產(chǎn)生兩次復(fù)制。
  • 當(dāng) channel 緩存隊(duì)列已滿時(shí),將當(dāng)前 goroutine 加入 send 隊(duì)列并阻塞。

所以,開頭的面試題就有了答案:

讀:

讀已經(jīng)關(guān)閉的 chan,能一直讀到內(nèi)容,但是讀到的內(nèi)容根據(jù)通道內(nèi)關(guān)閉前是否有元素而不同。

如果 chan 關(guān)閉前,buffer 內(nèi)有元素還未讀,會(huì)正確讀到 chan 內(nèi)的值,且返回的第二個(gè) bool 值為 true;

如果 chan 關(guān)閉前,buffer 內(nèi)有元素已經(jīng)被讀完,chan 內(nèi)無值,返回 channel 元素的零值,第二個(gè) bool 值為 false。

寫:

寫已經(jīng)關(guān)閉的 chan 會(huì) panic。

總結(jié)

到此這篇關(guān)于Go中Channel發(fā)送和接收操作的文章就介紹到這了,更多相關(guān)Go Channel發(fā)送和接收內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • GoLang語法之標(biāo)準(zhǔn)庫fmt.Printf的使用

    GoLang語法之標(biāo)準(zhǔn)庫fmt.Printf的使用

    fmt包實(shí)現(xiàn)了類似C語言printf和scanf的格式化I/O,主要分為向外輸出內(nèi)容和獲取輸入內(nèi)容兩大部分,本文就來介紹一下GoLang語法之標(biāo)準(zhǔn)庫fmt.Printf的使用,感興趣的可以了解下
    2023-10-10
  • Go關(guān)鍵字defer的使用和底層實(shí)現(xiàn)

    Go關(guān)鍵字defer的使用和底層實(shí)現(xiàn)

    defer是Go語言的關(guān)鍵字,一般用于資源的釋放和異常的捕捉,defer語句后將其后面跟隨的語句進(jìn)行延遲處理,就是說在函數(shù)執(zhí)行完畢后再執(zhí)行調(diào)用,也就是return的ret指令之前,本文給大家介紹了Go關(guān)鍵字defer的使用和底層實(shí)現(xiàn),需要的朋友可以參考下
    2023-11-11
  • go 迭代string數(shù)組操作 go for string[]

    go 迭代string數(shù)組操作 go for string[]

    這篇文章主要介紹了go 迭代string數(shù)組操作 go for string[],具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 利用Golang解析json數(shù)據(jù)的方法示例

    利用Golang解析json數(shù)據(jù)的方法示例

    Go提供了原生的JSON庫,并且與語言本身有效的集成在了一起。下面這篇文章將給大家介紹關(guān)于利用Golang解析json數(shù)據(jù)的方法,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-07-07
  • 詳解Go語言中如何高效遍歷目錄

    詳解Go語言中如何高效遍歷目錄

    目錄遍歷是一個(gè)很常見的操作,它的使用場景有如文件目錄查看、文件系統(tǒng)清理、日志分析、項(xiàng)目構(gòu)建等,本文將詳細(xì)介紹在Go中幾種遍歷目錄文件的方法,需要的可以參考下
    2024-02-02
  • Golang實(shí)現(xiàn)協(xié)程超時(shí)控制的方式總結(jié)

    Golang實(shí)現(xiàn)協(xié)程超時(shí)控制的方式總結(jié)

    我們知道,go協(xié)程如果不做好處理,很容易造成內(nèi)存泄漏,所以對(duì)goroutine做超時(shí)控制,才能有效避免這種情況發(fā)生,本文為大家整理了兩個(gè)常見的Golang超時(shí)控制方法,需要的可以收藏一下
    2023-05-05
  • 盤點(diǎn)幾種Go語言開發(fā)的IDE

    盤點(diǎn)幾種Go語言開發(fā)的IDE

    Go語言作為一種新興的編程語言,近年來受到了越來越多的關(guān)注,它以其簡潔、高效和并發(fā)性能而聞名,被廣泛應(yīng)用于各種軟件開發(fā)項(xiàng)目中,本文將介紹幾種常用的Go語言IDE,并對(duì)它們進(jìn)行比較,幫助開發(fā)者根據(jù)自己的需求選擇合適的工具,需要的朋友可以參考下
    2023-11-11
  • Golang通脈之流程控制詳情

    Golang通脈之流程控制詳情

    這篇文章主要介紹了Golang通脈之流程控制,流程控制是每種編程語言控制邏輯走向和執(zhí)行次序的重要部分,Go語言中最常用的流程控制有if和for,而switch和goto主要是為了簡化代碼,下面文章將詳細(xì)介紹改該內(nèi)容,需要的朋友可以參考一下
    2021-10-10
  • goland設(shè)置控制臺(tái)折疊效果

    goland設(shè)置控制臺(tái)折疊效果

    這篇文章主要介紹了goland設(shè)置控制臺(tái)折疊效果,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • golang使用通道時(shí)需要注意的一些問題

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

    本文主要介紹了golang使用通道時(shí)需要注意的一些問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論

盐源县| 望谟县| 嘉峪关市| 青阳县| 乐陵市| 綦江县| 台北市| 南和县| 汉中市| 巴东县| 黄山市| 视频| 白河县| 聂拉木县| 黑龙江省| 静乐县| 彭州市| 青阳县| 屏边| 平昌县| 友谊县| 贵定县| 襄城县| 通河县| 遂宁市| 固安县| 泗水县| 鞍山市| 贵州省| 星座| 万载县| 西畴县| 苍溪县| 图木舒克市| 仙居县| 保山市| 台湾省| 天长市| 洞口县| 昌吉市| 门头沟区|