亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入理解golang chan的使用

 更新時間:2022年06月02日 09:30:54   作者:煙草的香味  
本文主要介紹了golang chan的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

之前在看golang多線程通信的時候, 看到了go 的管道. 當時就覺得這玩意很神奇, 因為之前接觸過的不管是php, java, Python, js, c等等, 都沒有這玩意, 第一次見面, 難免勾起我的好奇心. 所以就想著看一看它具體是什么東西. 很明顯, 管道是go實現(xiàn)在語言層面的功能, 所以我以為需要去翻他的源碼了. 雖然最終沒有翻到C的層次, 不過還是受益匪淺.

見真身

結構體

要想知道他是什么東西, 沒什么比直接看他的定義更加直接的了. 但是其定義在哪里么? 去哪里找呢? 還記得我們是如何創(chuàng)建chan的么? make方法. 但是當我找過去的時候, 發(fā)現(xiàn)make方法只是一個函數的聲明.

這, 還是沒有函數的具體實現(xiàn)啊. 匯編看一下. 編寫以下內容:

package main

func main() {
	_ = make(chan int)
}

執(zhí)行命令:

go tool compile -N -l -S main.go

雖然匯編咱看不懂, 但是其中有一行還是引起了我的注意.

make調用了runtime.makechan. 漂亮, 就找他.

找到他了, 是hchan指針對象. 整理了一下對象的字段(不過人家自己也有注釋的):

// 其內部維護了一個循環(huán)隊列(數組), 用于管理發(fā)送與接收的緩存數據. 
type hchan struct {
  // 隊列中元素個數
	qcount   uint
  // 隊列的大小(數組長度)
	dataqsiz uint
  // 指向底層的緩存隊列, 是一個可以指向任意類型的指針. 
	buf      unsafe.Pointer
  // 管道每個元素的大小
	elemsize uint16
  // 是否被關閉了
	closed   uint32
  // 管道的元素類型
	elemtype *_type
  // 當前可以發(fā)送的元素索引(隊尾)
	sendx    uint  
  // 當前可以接收的元素索引(隊首)
	recvx    uint  
  // 當前等待接收數據的 goroutine 隊列
	recvq    waitq
  // 當前等待發(fā)送數據的 goroutine 隊列
	sendq    waitq 
	// 鎖, 用來保證管道的每個操作都是原子性的. 
	lock mutex
}

可以看的出來, 管道簡單說就是一個隊列加一把鎖.

發(fā)送數據

依舊使用剛才的方法分析, 發(fā)送數據時調用了runtime.chansend1 函數. 其實現(xiàn)簡單易懂:

然后查看真正實現(xiàn), 函數步驟如下(個人理解, 有一些 test 使用的代碼被我刪掉了. ):

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")
	}
	// 常量判斷, 恒為 false, 應該是開發(fā)時調試用的. 
	if debugChan {
		print("chansend: chan=", c, "\n")
	}
	// 常量, 恒為 false, 沒看懂這個判斷
	if raceenabled {
		racereadpc(c.raceaddr(), callerpc, funcPC(chansend))
	}
  // 若當前操作不阻塞, 且管道還沒有關閉時判斷
  // 當前隊列容量為0且沒有等待接收數據的 或 當前隊列容量不為0且隊列已滿
  // 那么問題來了, 什么時候不加鎖呢? select 的時候. 可以在不阻塞的時候快速返回
	if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
		(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
		return false
	}
	// 上鎖, 保證操作的原子性
	lock(&c.lock)
	// 若管道已經關閉, 報錯
	if c.closed != 0 {
		unlock(&c.lock)
		panic(plainError("send on closed channel"))
	}
	// 從接受者隊列獲取一個接受者, 若存在, 數據直接發(fā)送, 不走緩存, 提高效率
	if sg := c.recvq.dequeue(); sg != nil {
		send(c, sg, ep, func() { unlock(&c.lock) }, 3)
		return true
	}
	// 若緩存為滿, 則將數據放到緩存中排隊
	if c.qcount < c.dataqsiz {
    // 取出對尾的地址
		qp := chanbuf(c, c.sendx)
    // 將ep 的內容拷貝到 ap 地址
		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
	}
	// 當走到這里, 說明數據沒有成功發(fā)送, 且需要阻塞等待. 
  // 以下代碼沒看懂, 不過可以肯定的是, 其操作為阻塞當前協(xié)程, 等待發(fā)送數據
	gp := getg()
	mysg := acquireSudog()
	mysg.releasetime = 0
	if t0 != 0 {
		mysg.releasetime = -1
	}
	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)
	KeepAlive(ep)
	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
}

雖然最終阻塞的地方沒看太明白, 不過發(fā)送數據的大體流程很清楚:

  • 若無需阻塞且不能發(fā)送數據, 返回失敗
  • 若存在接收者, 直接發(fā)送數據
  • 若存在緩存, 將數據放到緩存中
  • 若無需阻塞, 返回失敗
  • 阻塞等待發(fā)送數據

其中不加鎖的操作, 在看到selectnbsend函數的注釋時如下:

// compiler implements
//
//	select {
//	case c <- v:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if selectnbsend(c, v) {
//		... foo
//	} else {
//		... bar
//	}
//
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
	return chansend(c, elem, false, getcallerpc())
}

看這意思, select關鍵字有點類似于語法糖, 其內部會轉換成調用selectnbsend函數的簡單if判斷.

接收數據

至于接收數據的方法, 其內部實現(xiàn)與發(fā)送大同小異. runtime.chanrecv 方法.

源碼簡單看了一下, 雖理解不深, 但對channel也有了大體的認識.

上手

簡單對channel的使用總結一下.

定義

// 創(chuàng)建普通的管道類型, 非緩沖
a := make(chan int)
// 創(chuàng)建緩沖區(qū)大小為10的管道
b := make(chan int, 10)
// 創(chuàng)建只用來發(fā)送的管道
c := make(chan<- int)
// 創(chuàng)建只用來接收的管道
d := make(<-chan int)
// eg: 只用來接收的管道, 每秒一個
e := time.After(time.Second)

發(fā)送與接收

// 接收數據
a := <- ch
b, ok := <- ch
// 發(fā)送數據
ch <- 2

最后, 看了一圈, 感覺channel并不是很復雜, 就是一個隊列, 一端接受, 一端發(fā)送. 不過其對多協(xié)程處理做了很多優(yōu)化. 與協(xié)程配合, 靈活使用的話, 應該會有不錯的效果.

到此這篇關于深入理解golang chan的使用的文章就介紹到這了,更多相關golang chan內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Go語言tunny的workerWrapper使用教程示例

    Go語言tunny的workerWrapper使用教程示例

    這篇文章主要為大家介紹了Go語言tunny的workerWrapper使用教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • Golang實現(xiàn)將中文轉化為拼音

    Golang實現(xiàn)將中文轉化為拼音

    這篇文章主要為大家詳細介紹了如何通過Golang實現(xiàn)將中文轉化為拼音功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-02-02
  • go運算符對變量和值執(zhí)行操作示例詳解

    go運算符對變量和值執(zhí)行操作示例詳解

    這篇文章主要為大家介紹了go運算符對變量和值執(zhí)行操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • 一文帶你搞明白Go中的面向對象

    一文帶你搞明白Go中的面向對象

    這篇文章主要為大家詳細介紹了Go語言中面向對象的相關知識,文中的示例代碼講解詳細,對我們深入了解Go語言有一定的幫助,需要的可以參考一下
    2023-07-07
  • graphql---go http請求使用詳解

    graphql---go http請求使用詳解

    這篇文章主要介紹了graphql---go http請求使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • golang微服務框架kratos實現(xiàn)Socket.IO服務的方法

    golang微服務框架kratos實現(xiàn)Socket.IO服務的方法

    本文主要介紹了golang微服務框架kratos實現(xiàn)Socket.IO服務的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • Golang實現(xiàn)http server提供壓縮文件下載功能

    Golang實現(xiàn)http server提供壓縮文件下載功能

    這篇文章主要介紹了Golang實現(xiàn)http server提供壓縮文件下載功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • 盤點幾種Go語言開發(fā)的IDE

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

    Go語言作為一種新興的編程語言,近年來受到了越來越多的關注,它以其簡潔、高效和并發(fā)性能而聞名,被廣泛應用于各種軟件開發(fā)項目中,本文將介紹幾種常用的Go語言IDE,并對它們進行比較,幫助開發(fā)者根據自己的需求選擇合適的工具,需要的朋友可以參考下
    2023-11-11
  • go語言處理JSON和XML數據示例解析

    go語言處理JSON和XML數據示例解析

    這篇文章主要介紹了go語言處理JSON和XML數據的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Go中strings的常用方法詳解

    Go中strings的常用方法詳解

    這篇文章主要介紹了Go中strings的常用方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03

最新評論