真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

本篇文章給大家分享的是有關(guān)Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么,小編覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供萬全網(wǎng)站建設(shè)、萬全做網(wǎng)站、萬全網(wǎng)站設(shè)計、萬全網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、萬全企業(yè)網(wǎng)站模板建站服務(wù),十載萬全做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。

channel 的需求描述

為了理解這些數(shù)據(jù)結(jié)構(gòu)解決了什么問題,我們先來做個簡單的回顧,看看為什么需要這兩個數(shù)據(jù)結(jié)構(gòu),他們解決了什么問題。我們知道 goroutine是用戶態(tài)的線程,不同的 goroutine之間是有消息傳遞這個需求的。在原始的進程與線程(系統(tǒng)線程)編程中我們會采用管道的方式,而 channel 就是用戶態(tài)線程傳遞消息的管道實現(xiàn),并且是類型安全的。

既然 channel 是一個管道,用來滿足不同 goroutine間交換消息的。那么實現(xiàn)這樣一個管道要怎么做呢?

來看看我們?nèi)粘鬟f消息的需求:

  1. 能夠有多個     goroutine向     channel 進行讀寫,并且保證沒有競爭問題,需要用     隊列來管理阻塞的     goroutine,解決競爭問題;
  2. 需要管理發(fā)送到     channel 的消息(有緩沖、無緩沖),對于有緩存的     channel 可以采用     循環(huán)隊列來管理多個消息。

當然上面的需求是經(jīng)過簡化的,比如 channel 還需要具備阻塞、喚醒 goroutine的能力,不過為了本文我們更加專注焦點問題,先只關(guān)注上面兩個問題。

 

channel 的數(shù)據(jù)結(jié)構(gòu)

接下來我們分析一下 channel 在實際運行中,它的結(jié)構(gòu)體是怎么樣的。當然這又分為兩種類型,有緩沖與無緩沖的。我們先來看一個無緩沖的情況。

 

無緩沖

先把示例代碼貼出來。就是兩個讀的 goroutine被阻塞在一個無緩沖的 channel 上。

func main() {
 ch := make(chan int) // 無緩沖

 go goRoutineA(ch)
 go goRoutineB(ch)
 ch <- 1

 time.Sleep(time.Second * 1)
}

func goRoutineA(ch chan int) {
 v := <-ch
 fmt.Printf("A received data: %d\n", v)
}

func goRoutineB(ch chan int) {
 v := <-ch
 fmt.Printf("B received data: %d\n", v)
}
 

來看看當代碼執(zhí)行到 ch <- 1 這一行之后 channel 的結(jié)構(gòu)體被填充成什么樣子了!

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

無緩沖 channel

注意其中 buf 字段可存儲的長度是0,這是因為 無緩沖 channel不會用到循環(huán)隊列來存儲數(shù)據(jù)。它一定是等讀、寫 goroutine 都準備好了,然后直接把數(shù)據(jù)交給對方。我們用一副圖來看一下無緩沖的數(shù)據(jù)交換過程。

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

交換數(shù)據(jù)

上圖描述的是數(shù)據(jù)交換過程,再看一下讀 goroutine 被阻塞的結(jié)構(gòu)示意圖。被阻塞的 goroutine 會掛載到對應(yīng)的隊列上,該隊列是一個雙端隊列。

上面的例子,由于兩個讀 goroutine 在啟動的時候,寫還沒有準備好,因此讀全部被掛起在隊列中;當有寫goroutine準備好的時候,由于此時讀已經(jīng)就緒,因此寫不會阻塞,掛起放到 sendq 中。大家可以修改上面的代碼,自己看一下寫阻塞,讀立馬執(zhí)行的情況。

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

結(jié)構(gòu)示例
 

有緩沖

我們將上面的代碼改成有緩沖的通道,然后再來看看有緩沖的情況。

func main() {
 ch := make(chan int, 3) // 有緩沖

 // 都不會阻塞
 ch <- 1
 ch <- 2
    ch <- 3
    // 會阻塞,被掛起到 sendq 中
 go func() {
  ch <- 4
 }()

 // 只是為了debug
 var a int
 fmt.Println(a)

 go goRoutineA(ch)
 go goRoutineA(ch)
 go goRoutineB(ch)
 go goRoutineB(ch) // 猜猜這里會被掛起嗎?

 time.Sleep(time.Second * 2)
}

func goRoutineA(ch chan int) {
 v := <-ch
 fmt.Printf("A received data: %d\n", v)
}

func goRoutineB(ch chan int) {
 v := <-ch
 fmt.Printf("B received data: %d\n", v)
}
 

貼出執(zhí)行到第一行的 go goRoutineA(ch) 時, hchan 的結(jié)構(gòu)填充情況。

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

有緩沖 channel

在這里可以看到緩沖的大小是3,由于增加了緩沖,只要寫 goroutine 沒有把緩沖寫滿,則不會導(dǎo)致協(xié)程阻塞。但是一旦緩沖沒有多余的空間,則會把寫 goroutine 掛起到 sendq 中,直到有空間時將他喚醒(還有其它喚醒的場景,這一略過)。

其實有緩沖的 channel,就是把同步的通信變?yōu)榱水惒降耐ㄐ拧懙?channel 不需要關(guān)注讀 channel,只要有空間它就寫;而讀也一樣,只要有數(shù)據(jù)就正常讀就可以,如果沒有就掛起到隊列中,等待被喚醒。下圖形象的展示了有緩沖 channel 是如何交換數(shù)據(jù)的。

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

交換數(shù)據(jù)

我們再來用圖的形式看一下此時結(jié)構(gòu)體的樣子,這里圖有些偷懶,只是在上面圖的基礎(chǔ)上增加了循環(huán)隊列部分的描述,實際到該例子中,讀 goroutine時不會被阻塞的,看的時候需要注意這一點。

Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么

結(jié)構(gòu)示例
 

循環(huán)隊列

今天最重要的是理解 channel 中兩個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)。為了下一講閱讀源碼做準備,我把 channel 中的循環(huán)隊列部分的代碼抽象出來了。

// 隊列滿了
var ErrQFull = errors.New("circular is full")

// 沒有值
var ErrQEmpty = errors.New("circular is empty")

// 定義循環(huán)隊列
// 如何確定隊空,還是隊滿?q.sendx = (q.sendx+1) % q.dataqsiz
type queue struct {
 buf      []int // 隊列元素存儲
 dataqsiz uint  // circular 隊列長度
 qcount   uint  // 有多少元素在buf中 qcount = len(buf)
 sendx    uint  // 可以理解為隊尾指針,向隊列寫入數(shù)據(jù)
 recvx    uint  // 可以理解為隊頭指針,從隊列讀取數(shù)據(jù)
}

func makeQ(size int) *queue {
 q := &queue{
  dataqsiz: uint(size),
  buf:      nil,
 }

 q.buf = make([]int, q.dataqsiz)

 return q
}

// 向buf中寫入數(shù)據(jù)
// 請看 chansend 函數(shù)
func (c *queue) insert(ele int) error {
 // 檢查隊列是否有空間
 if c.dataqsiz > 0 && c.qcount == c.dataqsiz {
  return ErrQFull
 }

 // 存入數(shù)據(jù)
 c.buf[c.sendx] = ele
 c.sendx++                  // 尾指針后移
 if c.sendx == c.dataqsiz { // 如果相等,說明隊列寫滿了,sendx放到開始位置
  c.sendx = 0
 }
 c.qcount++

 return nil
}

// 從buf中讀取數(shù)據(jù)
func (c *queue) read() (int, error) {
 // 隊列中沒有數(shù)據(jù)了
 if c.dataqsiz > 0 && c.qcount == 0 {
  return 0, ErrQEmpty
 }

 ret := c.buf[c.recvx] // 取出元素
 c.buf[c.recvx] = 0
 c.recvx++
 if c.recvx == c.dataqsiz { // 如果相等,說明寫到末尾了,recvx放到開始位置
  c.recvx = 0
 }
 c.qcount--

 return ret, nil
}

以上就是Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降?。希望你能通過這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。


網(wǎng)站題目:Golang中Channel數(shù)據(jù)結(jié)構(gòu)的作用是什么
轉(zhuǎn)載來于:http://weahome.cn/article/jhdpjs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部