go源碼閱讀——chan.go

【博文目錄>>>】 【項目地址>>>】

chan.go是go語言通道實現,通道結構的定義,接收和發送的操作都此文件中實現。

通道的結構

hchan是通道表示的基本結構,其內容表示如下:
一些特殊情況

  • 當dataqsiz=0時:說明這是一個無緩衝對列
  • 當dataqsiz>0時,說明是一個緩衝對列
type hchan struct {
    qcount   uint               // 隊列中的數據總數
    dataqsiz uint               // 循環隊列的大小
    buf      unsafe.Pointer     // 指向dataqsiz數組中的一個元素
    elemsize uint16
    closed   uint32             // 非0表示通道已經關閉
    elemtype *_type             // 元素類型
    sendx    uint               // 發送索引
    recvx    uint               // 接收索引
    recvq    waitq              // 所有等待的接收者
    sendq    waitq              // 所有等待的發送者

    // 鎖保護hchan中的所有字段,以及此通道上阻止的sudogs中的多個字段。
    // 保持該鎖狀態時,請勿更改另一個G的狀態(特別是不要準備好G),因爲這會因堆棧收縮而死鎖。
    // Question: sudogs?參見runtime/runtime2.go中的sudog結構
    lock mutex
}

通道的發送和接收

通道的發送和接收都是使用了一個名爲waitq的等待對例,每個接收者和發送者都是一個sudog結構,此構在runtiem/runtime2.go文件中定義,在之後的源碼分析中會仔細說明。

type waitq struct {
	first *sudog    // 隊列頭
	last  *sudog    // 隊列尾
}

通道的對齊方法式

從文件的源碼中可以知道,在內存中通道是以8字節的方式進行對齊的,內存分配不是8個字節會自動對對齊

const (
	maxAlign  = 8 // 8字節對齊
	hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1))
	debugChan = false
)

通道的使用

通道的創建的方法

在源碼中通道的創建有三個實現方法

  • func reflect_makechan(t *chantype, size int) *hchan {…}
  • func makechan64(t *chantype, size int64) *hchan {…}
  • func makechan(t *chantype, size int) *hchan {…}
    其本質是調用最後一個通道的創建方法。

通道的創建過程

方法func makechan(t *chantype, size int) *hchan {...}說明了通道的整個創建過程。

/**
 * 創建通道
 * @param t 通道類型指針
 * @param size 通道大小,0表示無緩衝通道
 * @return
 **/
func makechan(t *chantype, size int) *hchan {...}

通道創建要通過以過幾個步驟。

  • 1、檢查通道元素類型的size,如果elem.size >= 1<<16,則創建失敗。
  • 2、檢查通道和元素的對齊,如果hchanSize%maxAlign != 0 || elem.align > maxAlign,則通道創建失敗
  • 3、計算通道需要分配的內存,如果overflow || mem > maxAlloc-hchanSize || size < 0,則通道創建失敗
    • 3.1、overflow:表示內存內存計算有溢出
    • 3.2、mem > maxAlloc-hchanSize:表示內存分配置超出了限制
    • 3.3、size < 0:表示通道緩衝爲負數
  • 4、根據不同條件進行內存分配
    • 4.1、mem == 0:隊列或者元素大小爲0,這是表示元素或者隊列大小爲0,直接分配hchanSize大小的內存,緩衝地址向自身
    • 4.2、elem.ptrdata == 0:元素不包含指針,直接分配hchanSize+mem大小的內存,並且緩衝地址向自身+hchanSize
    • 4.3、其他情況(元素包含指針):使用new的方法創建一個hchan,分配mem大小的內存,並且緩衝地址向內存分配的地址
  • 5、最後設置通道結構的其他值

向通道中發送數據

向通道中發送數據的方法一共有四個,如下所示

  • func chansend1(c *hchan, elem unsafe.Pointer) {…}
  • func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {…}
  • func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {…}
  • func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) {…}
    chansend1方法是go編譯代碼中c <- x的入口點,即當我們編寫代碼 c <- x時,就是調用此方法。chansend1方法本質還是調用chansend方法進行處理
    這四個方法的調用關係:chansend1 -> chansend -> send -> sendDirect

chansend方法

chansend方法的執行代表了事個數據的發送過程,方法簽名如下:

/**
  * 通用單通道發送/接收
  * 如果block不爲nil,則協議將不會休眠,但如果無法完成則返回。
  *
  * 當涉及休眠的通道已關閉時,可以使用g.param == nil喚醒休眠。
  * 最容易循環並重新運行該操作; 我們將看到它現已關閉。
  * @param c 通道對象
  * @param ep 元素指針
  * @param block 是否阻塞
  * @param callerpc 調用者指針
  * @return bool true:表示發送成功
  **/
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {...}

通道數據發送的處理過程:

  • 1、如果通道爲空
    • 1.1、通道是非阻塞的,則返回false,表示發送失敗,方法結束
    • 1.2、通道是阻塞的,則會阻塞當前goroutine,同時會執行一個throw方法,方法結束
  • 2、檢查通道和數據狀態,2.1,2.2,2.3同時滿足時發關失敗,方法結束
    • 2.1、!block:通道非阻塞
    • 2.2、c.closed == 0:通道未關閉
    • 2.3、((c.dataqsiz == 0 && c.recvq.first == nil) || (c.dataqsiz > 0 && c.qcount == c.dataqsiz))
      • 2.3.1、(c.dataqsiz == 0 && c.recvq.first == nil):通道中沒有數據,並且沒有接收者
      • 2.3.2、(c.dataqsiz > 0 && c.qcount == c.dataqsiz):通道中有數據,並且已經滿了
  • 3、對通道進行鎖定
  • 4、判斷通道是否已經關閉,如果已經關閉,就解鎖通道,並且拋出panic。方法結束
  • 5、在接收者隊列中找出一個最先入隊的接收者,如果有,就調用send方法進行發送,返回true,方法結束
  • 6、如果沒有找到接收者,並且c.qcount < c.dataqsiz,即通道的發送緩衝區未滿,將要發送的數據拷貝到通道緩衝區,更新相關的計數據信息,並釋放鎖,返回true,方法結束
  • 7、沒有找到接收都,並且沒有緩衝可用,非阻塞方式,則解鎖通道,返回false,發送失敗
  • 8、沒有找到接收者,並且沒有緩衝可用,阻塞方式。獲取gp(g)和mysg(sudog),並且將發送數據掛到mysg上,將mysg加到發送隊列。調用gopark訪求將當前goroutine阻塞,直到被恢復。
  • 9、在恢復後我們還要將發送數據保活,以確保數據正確被接收者複製出去了。
  • 10、檢查goroutine狀態,如果mysg != gp.waiting說明被破壞了,執行throw,方法結束
  • 11、如果gp.param == nil,說明喚醒有問題,
    • 11.1、如果通道未關閉,則說明是僞喚醒,執行throw方法結束
    • 11.2、如果通道關閉,則panic,在關閉的通道中進行了發送消息。
  • 12、最後是清理數據,並且釋放mysg

其他相關方法

/**
 * 編譯器實現,將goroutine的select receive(v = <-c)語句轉成對應的方法執行
 * select {
 * case v = <-c:
 * 	... foo
 * default:
 * 	... bar
 * }
 * 
 * => (實際對應)
 * 
 * if selectnbrecv(&v, c) {
 * 	... foo
 * } else {
 * 	... bar
 * }
 */
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {...}

從通道中接收數據

chan.go文件中一共有五個方法實現用於接收通道內容,他們分別是:

  • func chanrecv1(c *hchan, elem unsafe.Pointer) {…},
  • func chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {…}
  • func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {…}
  • func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {…}
  • func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {…}
    其調用鏈 (chanrecv1|chanrecv2) -> chanrecv -> recv -> recvDirect
    其中chanrecv1方法是go代碼<-c的入口點

chanrecv方法

chanrecv方法實現的通道接收的主要功能,其方法簽名:

/**
 * 通用單通道發送/接收
 * 如果block不爲nil,則協議將不會休眠,但如果無法完成則返回。
 *
 * 當涉及休眠的通道已關閉時,可以使用g.param == nil喚醒休眠。
 * 最容易循環並重新運行該操作; 我們將看到它現已關閉。
 * @param c 通道對象
 * @param ep 元素指針
 * @param block 是否阻塞
 * @param callerpc 調用者指針
 * @return bool true:表示發送成功
 **/
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {...}

通道數據接收的處理過程如下:

  • 1、如通道接收通道爲nil
    • 1.1、如果接收是非阻塞的,則返回false,方法結束
    • 1.2、如果接收是阻塞的,則阻塞goroutine,即此goroutine會永久阻塞
  • 2、當2.1,2.2,2.3同時滿足時,則返回false,方法結束
    • 2.1、!block == true,即非阻塞
    • 2.2、c.closed == 0,通道未關閉
    • 2.3、2.3.1,2.3.2兩個滿足其中一個
      • 2.3.1、c.dataqsiz == 0 && c.sendq.first == nil,通道中沒有數據,並且沒有發送者
      • 2.3.2、c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0,通道中有空間,但沒有數據
  • 3、加鎖
  • 4、如果通道已經關閉,並且沒有數據,則解鎖,進行內存清理,返回(true, false),方法結束
  • 5、從發送隊列中取隊頭髮送者,如果不爲空,就用發送者發送的數據,返回(true, true),方法結束
  • 6、沒有發送者,數據隊列不爲空(c.qcount > 0),直接從數據隊列中接收數據,並且更新隊列計數和接收索引指針,然後複製數據,清理內存,釋放鎖,最後返回(true, true),方法結束
  • 7、沒有發送者,並且隊列爲空,並且是非阻塞狀態,則釋放鎖,返回(false, false),方法結束
  • 8、沒有找到發送都,並且沒有緩衝可用,阻塞方式。獲取gp(g)和mysg(sudog),並且將接收數據指針掛到mysg上,將mysg加到接收隊列。調用gopark訪求將當前goroutine阻塞,直到被恢復。
  • 9、在恢復後我們還是判斷是否是意外喚醒,如果是,就panic,方法結束
  • 10、進行清理工作,釋放sudog,返回(true, !closed)

recv方法

通道另一個核心方法就是recv,其方法簽名如下:

/**
 * recv在完整通道c上處理接收操作。
 * 有2個部分:
 *      1)將發送方sg發送的值放入通道中,並喚醒發送方以繼續進行。
 *      2)接收方接收到的值(當前G)被寫入ep。
 * 對於同步通道,兩個值相同。
 * 對於異步通道,接收者從通道緩衝區獲取數據,而發送者的數據放入通道緩衝區。
 * 頻道c必須已滿且已鎖定。 recv用unlockf解鎖c。
 * sg必須已經從c中出隊。
 * 非nil必須指向堆或調用者的堆棧。
 * @param c 通道對象針對
 * @param sg sudog指針
 * @param ep 用戶接收元素的指針
 * @param unlockf 解鎖函數
 * @param skip
 * @return
 **/
func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {...}

recv接收數據的過程

  • 1、對於無緩衝通道(c.dataqsiz == 0),當數據指針不空的時候,直接拷貝發送指針所指的數據
  • 2、對於緩衝通道,將數據寫入到緩衝中,並且更新緩衝計數,接收索引,發送索引
  • 3、進行goroutine相關處理,釋放鎖,並且將當前goroutine置於等待

其他相關方法

/**
 * 編譯器實現,將goroutine的select receive(v = <-c)語句轉成對應的方法執行
 * select {
 * case v = <-c:
 * 	... foo
 * default:
 * 	... bar
 * }
 * 
 * => (實際對應)
 * 
 * if selectnbrecv(&v, c) {
 * 	... foo
 * } else {
 * 	... bar
 * }
 **/
func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected bool) {...}

/**
 * 編譯器實現,將goroutine的select receive(case v, ok = <-c:)語句轉成對應的方法執行
 * select {
 * case v, ok = <-c:
 *  ... foo
 * default:
 *  ... bar
 * }
 *
 * => (實際對應)
 *
 * if c != nil && selectnbrecv2(&v, &ok, c) {
 *  ... foo
 * } else {
 *  ... bar
 * }
 **/
func selectnbrecv2(elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {...}

通道關閉

chan.go中只有一個關閉通道的方法

  • func closechan(c *hchan) {…}
    通過的關閉過程
  • 1、如果通道爲nil,則painc,方法結束
  • 2、鎖定通道
  • 3、如果通道已經關閉了,解鎖通道,並且panic,方法結束,即通道只能關閉一次
  • 4、標記通道已經關閉
  • 5、釋放所有的接收者,將接收者入gList隊列
  • 6、釋放所有的發送者,將發送者入gList隊列
  • 7、釋放鎖
  • 8、將gLsit中的元素都標記成進入goready狀態

源碼

package runtime

// This file contains the implementation of Go channels.

// Invariants:
//  At least one of c.sendq and c.recvq is empty,
//  except for the case of an unbuffered channel with a single goroutine
//  blocked on it for both sending and receiving using a select statement,
//  in which case the length of c.sendq and c.recvq is limited only by the
//  size of the select statement.
//
// For buffered channels, also:
//  c.qcount > 0 implies that c.recvq is empty.
//  c.qcount < c.dataqsiz implies that c.sendq is empty.
/**
 * 此文件包含Go渠道的實現。此包中所使用的類型大部分定義在:runtime/type.go文件中
 *
 * 不變量:
 * c.sendq和c.recvq中的至少一個爲空,但在無緩衝通道上阻塞了單個goroutine以便使用select語句發送和接收的情況除外,在這種情況下,
 * c.sendq的長度 而c.recvq僅受select語句的大小限制。
 *
 * 對於緩衝通道,還:
 * c.qcount> 0表示c.recvq爲空。
 * c.qcount <c.dataqsiz表示c.sendq爲空。
 */
import (
	"runtime/internal/atomic"
	"runtime/internal/math"
	"unsafe"
)

const (
	maxAlign  = 8 // 8字節對齊
	hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1))
	debugChan = false
)

type hchan struct {
	qcount   uint           // total data in the queue // 隊列中的數據總數
	dataqsiz uint           // size of the circular queue   // 循環隊列的大小
	buf      unsafe.Pointer // points to an array of dataqsiz elements // 指向dataqsiz數組中的一個元素
	elemsize uint16
	closed   uint32  // 非0表示通道已經關閉
	elemtype *_type // element type // 元素類型
	sendx    uint   // send index   // 發送索引
	recvx    uint   // receive index // 接收索引
	recvq    waitq  // list of recv waiters // 所有等待的接收者
	sendq    waitq  // list of send waiters // 所有等待的發送者

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	// 鎖保護hchan中的所有字段,以及此通道上阻止的sudogs中的多個字段。
    // 保持該鎖狀態時,請勿更改另一個G的狀態(特別是不要準備好G),因爲這會因堆棧收縮而死鎖。
    // Question: sudogs?參見runtime/runtime2.go中的sudog結構
	lock mutex
}

/**
 * 等待隊列數據結棍
 */
type waitq struct {
	first *sudog    // 隊列頭
	last  *sudog    // 隊列尾
}

//go:linkname reflect_makechan reflect.makechan
/**
 * 通過反射創建通道,
 * @param
 * @return
 **/
func reflect_makechan(t *chantype, size int) *hchan {
	return makechan(t, size) // 在reflect/value.go/makechan方法中實現
}

/**
 * 創建通道
 * @param t 通道類型
 * @param size 看上去是支持64位int,本質上只支持int類型
 * @return
 **/
func makechan64(t *chantype, size int64) *hchan {
	if int64(int(size)) != size { // 說明有溢出
		panic(plainError("makechan: size out of range"))
	}

	return makechan(t, int(size))
}

/**
 * 創建通道
 * @param t 通道類型指針
 * @param size 通道大小,0表示無緩衝通道
 * @return
 **/
func makechan(t *chantype, size int) *hchan {
	elem := t.elem

	// compiler checks this but be safe.
	// 編譯器對此進行檢查,但很安全。
	if elem.size >= 1<<16 {
		throw("makechan: invalid channel element type")
	}
	// 非8字節對齊
	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
		throw("makechan: bad alignment")
	}

    // MulUintptr返回a * b以及乘法是否溢出。 在受支持的平臺上,這是編譯器固有的功能。
	mem, overflow := math.MulUintptr(elem.size, uintptr(size))
	// 發生溢出,或者分配內存超限制,或者size<0
	if overflow || mem > maxAlloc-hchanSize || size < 0 {
		panic(plainError("makechan: size out of range"))
	}

	// Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.
	// buf points into the same allocation, elemtype is persistent.
	// SudoG's are referenced from their owning thread so they can't be collected.
	// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.
	// 當存儲在buf中的元素不包含指針時,Hchan不包含GC感興趣的指針。 buf指向相同的分配,
	// elemtype是持久的。 SudoG是從它們自己的線程中引用的,因此無法收集它們。
    // TODO(dvyukov,rlh):重新考慮何時收集器可以移動分配的對象。
	var c *hchan
	switch {
	case mem == 0: // 不需要分配置內存空間
		// Queue or element size is zero.
	    // 隊列或元素大小爲零。
	    // mallocgc分配一個大小爲size字節的對象。 小對象是從每個P緩存的空閒列表中分配的。
	    // 大對象(> 32 kB)直接從堆中分配。
		c = (*hchan)(mallocgc(hchanSize, nil, true))
		// Race detector uses this location for synchronization.
		// 競態探測器使用此位置進行同步。
		c.buf = c.raceaddr()
	case elem.ptrdata == 0: // 無指針數據
		// Elements do not contain pointers.
		// Allocate hchan and buf in one call.
		// 元素不包含指針。 在一次調用中分配hchan和buf。
		c = (*hchan)(mallocgc(hchanSize+mem, nil, true))
		c.buf = add(unsafe.Pointer(c), hchanSize) // 修改數據指針地址
	default:
		// Elements contain pointers.
	    // 元素包含指針。
		c = new(hchan)
		c.buf = mallocgc(mem, elem, true)
	}

	c.elemsize = uint16(elem.size) // 設置元素大小
	c.elemtype = elem   // 設置元素類型
	c.dataqsiz = uint(size) // 設置通道大小

	if debugChan {
		print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n")
	}
	return c
}

// chanbuf(c, i) is pointer to the i'th slot in the buffer.
/**
 * chanbuf(c, i) 返回指向緩衝區中第i個槽值的指針。
 * @param c 通道對象
 * @return i 第i個槽位
 **/
func chanbuf(c *hchan, i uint) unsafe.Pointer {
	return add(c.buf, uintptr(i)*uintptr(c.elemsize))
}

// entry point for c <- x from compiled code
//go:nosplit
/**
 * 編譯代碼中c <- x的入口點,即當我們編寫代碼 c <- x時,就是調用此方法
 * @param c 通道對象
 * @param elem 需要發送的元素
 * @return
 **/
func chansend1(c *hchan, elem unsafe.Pointer) {
	chansend(c, elem, true, getcallerpc())
}

/*
 * generic single channel send/recv
 * If block is not nil,
 * then the protocol will not
 * sleep but return if it could
 * not complete.
 *
 * sleep can wake up with g.param == nil
 * when a channel involved in the sleep has
 * been closed.  it is easiest to loop and re-run
 * the operation; we'll see that it's now closed.
 */
 /**
  * 通用單通道發送/接收
  * 如果block不爲nil,則協議將不會休眠,但如果無法完成則返回。
  *
  * 當涉及休眠的通道已關閉時,可以使用g.param == nil喚醒休眠。
  * 最容易循環並重新運行該操作; 我們將看到它現已關閉。
  * @param c 通道對象
  * @param ep 元素指針
  * @param block 是否阻塞
  * @param callerpc 調用者指針
  * @return bool true:表示發送成功
  **/
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
	if c == nil { // 通道已爲空
		if !block { // 非阻塞
			return false
		}
		// 將當前goroutine置於等待狀態並調用unlockf。 如果unlockf返回false,則繼續執行goroutine程序。
		// unlockf一定不能訪問此G的堆棧,因爲它可能在調用gopark和調用unlockf之間移動。
        // waitReason參數說明了goroutine已停止的原因。
        // 它顯示在堆棧跟蹤和堆轉儲中。
        // waitReason應具有唯一性和描述性。
        // 不要重複使用waitReason,請添加新的waitReason。
        // 更詳細的說明參見:runtime/proc.go和runtime/runtime2.go
		gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)
		throw("unreachable")
	}

	if debugChan { // 當前已經爲false
		print("chansend: chan=", c, "\n")
	}

	if raceenabled { // 當前已經爲false
		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.
	// 快速路徑:在沒有獲取鎖的情況下檢查失敗的非阻塞操作。
    //
    // 觀察到通道未關閉後,我們觀察到該通道尚未準備好發送。 這些觀察中的每一個都是單個字(word)大小的讀取
    // (根據通道的類型,取第一個c.closed和第二個c.recvq.first或c.qcount)。
    // 因爲關閉的通道無法從“準備發送”轉換爲“未準備發送”,所以即使通道在兩個觀測值之間處於關閉狀態,
    // 它們也隱含着兩者之間的一個時刻,即通道既未關閉又未關閉準備發送。 我們的行爲就好像我們當時在觀察該通道,
    // 並報告發送無法繼續進行。
    //
    // 如果在此處對讀取進行了重新排序,也是可以的:如果我們觀察到該通道尚未準備好發送,然後觀察到它沒有關閉,
    // 則意味着該通道在第一次觀察期間沒有關閉。
    // !block:非阻塞狀態
    // c.closed == 0:通道未關閉
    // (c.dataqsiz == 0 && c.recvq.first == nil):通道中沒有數據,並且沒有接收者
    // (c.dataqsiz > 0 && c.qcount == c.dataqsiz):通道中有數據,並且已經滿了
	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 { // 通道已經關閉,解鎖,拋出panic
		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 { // 此值已經爲false
			raceacquire(qp)
			racerelease(qp)
		}
		// 執行內存移動
		typedmemmove(c.elemtype, qp, ep)
		c.sendx++ // 指向下一個可以發送數據的空位
		if c.sendx == c.dataqsiz { // 已經達到了末尾
			c.sendx = 0 // 重新指向頭部
		}
		c.qcount++ // 通道總的數據加1
		unlock(&c.lock) // 解鎖
		return true // 說明發送成功
	}

    // 非阻塞,因爲找不到接收者,所以失敗
	if !block {
		unlock(&c.lock)
		return false
	}

	// Block on the channel. Some receiver will complete our operation for us.
    // 在通道上阻塞。一些接收器將爲我們完成操作。
    // getg將返回指向當前g的指針。獲取suodg
	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.
	// 在分配elem和將mysg入隊到gp.waitcopy可以找到它的地方之間沒有堆棧拆分。
	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.
	// 確保發送的值保持活動狀態,直到接收者將其複製出來。
	// sudog具有指向堆棧對象的指針,但是sudog不被視爲堆棧跟蹤器的根。
	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) // 釋放sudog
	return true
}

// send processes a send operation on an empty channel c.
// The value ep sent by the sender is copied to the receiver sg.
// The receiver is then woken up to go on its merry way.
// Channel c must be empty and locked.  send unlocks c with unlockf.
// sg must already be dequeued from c.
// ep must be non-nil and point to the heap or the caller's stack.
/**
 * send在空通道c上執行發送操作。
 * 將發送方發送的ep值複製到接收方sg。
 * 然後將接收器喚醒,繼續前進。
 * 頻道c必須爲空且已鎖定。 使用unlockf發送解鎖通道c。
 * sg必須已經從c中出隊。
 * ep必須爲非nil,並指向堆或調用者的堆棧。
 * @param c 通道對象
 * @param sg
 * @param ep 元素指針
 * @param unlockf 角鎖方法
 * @param skip
 * @return
 **/
func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
	if raceenabled { // 此值已經爲false
		if c.dataqsiz == 0 {
			racesync(c, sg)
		} else {
			// Pretend we go through the buffer, even though
			// we copy directly. Note that we need to increment
			// the head/tail locations only when raceenabled.
			qp := chanbuf(c, c.recvx)
			raceacquire(qp)
			racerelease(qp)
			raceacquireg(sg.g, qp)
			racereleaseg(sg.g, qp)
			c.recvx++
			if c.recvx == c.dataqsiz {
				c.recvx = 0
			}
			c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz
		}
	}
	if sg.elem != nil { // 元素不爲空,直接發送
		sendDirect(c.elemtype, sg, ep)
		sg.elem = nil
	}
	gp := sg.g
	unlockf()
	gp.param = unsafe.Pointer(sg)
	if sg.releasetime != 0 {
		sg.releasetime = cputicks()
	}
	goready(gp, skip+1) // Question: 這裏是用來做什麼?
}

// Sends and receives on unbuffered or empty-buffered channels are the
// only operations where one running goroutine writes to the stack of
// another running goroutine. The GC assumes that stack writes only
// happen when the goroutine is running and are only done by that
// goroutine. Using a write barrier is sufficient to make up for
// violating that assumption, but the write barrier has to work.
// typedmemmove will call bulkBarrierPreWrite, but the target bytes
// are not in the heap, so that will not help. We arrange to call
// memmove and typeBitsBulkBarrier instead.
/**
 * 在一個無緩衝通道或空緩衝通道上發送和接收是一個正在運行的goroutine寫入另一個正在運行的goroutine堆棧的唯一操作。
 * GC假定僅在goroutine運行時才發生堆棧寫入,並且僅由該goroutine完成。 使用寫屏障足以彌補違反該假設的缺點,
 * 但是寫屏障必須起作用。 typedmemmove將調用bulkBarrierPreWrite,但是目標字節不在堆中,因此這無濟於事。
 * 我們安排調用memmove和typeBitsBulkBarrier。
 * @param t 元素類型
 * @param sg
 * @param src 數據指針
 * @return
 **/
func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) {
	// src is on our stack, dst is a slot on another stack.
	// src在我們的棧上,dst是另一個棧上的位置。

	// Once we read sg.elem out of sg, it will no longer
	// be updated if the destination's stack gets copied (shrunk).
	// So make sure that no preemption points can happen between read & use.
	// 一旦我們從sg中讀取出sg.elem,如果目標堆棧被複制(縮小),它將不再被更新。
	// 因此,請確保在讀取和使用之間沒有任何搶佔點。
	dst := sg.elem
	// 帶屏障的寫操作,參見:runtime/mbitmap.go/typeBitsBulkBarrier方法
	typeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)
	// No need for cgo write barrier checks because dst is always
	// Go memory.
	// 不需要cgo寫屏障檢查,因爲dst始終是Go內存。
	memmove(dst, src, t.size)
}

/**
 * 直接接收通道數據
 * @param
 * @return
 **/
func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {
	// dst is on our stack or the heap, src is on another stack.
	// The channel is locked, so src will not move during this
	// operation.
	// dst在我們的棧或堆上,src在另一個棧上。 通道已鎖定,因此src在此操作期間將不會移動。
	src := sg.elem
	typeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.size)
	memmove(dst, src, t.size)
}
/**
 * 關閉通道
 * @param
 * @return
 **/
func closechan(c *hchan) {
	if c == nil { // 通道爲nil
		panic(plainError("close of nil channel"))
	}

	lock(&c.lock) // 鎖定
	if c.closed != 0 { // 通道已經關閉,再關閉就報panic
		unlock(&c.lock) // 解鎖
		panic(plainError("close of closed channel"))
	}

	if raceenabled { // 此值已經爲false
		callerpc := getcallerpc()
		racewritepc(c.raceaddr(), callerpc, funcPC(closechan))
		racerelease(c.raceaddr())
	}

	c.closed = 1 // 標記通道已經關閉

	var glist gList

	// release all readers // 釋放所有的接收者
	for {
		sg := c.recvq.dequeue() // 接收者隊列
		if sg == nil { // 隊列爲空
			break
		}
		if sg.elem != nil {
			typedmemclr(c.elemtype, sg.elem) // 進行內存清理
			sg.elem = nil
		}
		if sg.releasetime != 0 {
			sg.releasetime = cputicks()
		}
		gp := sg.g
		gp.param = nil // 參數清零
		if raceenabled {
			raceacquireg(gp, c.raceaddr())
		}
		glist.push(gp) // gp入隊頭
	}

	// release all writers (they will panic)
	// 釋放所有寫對象(他們會恐慌)
	for {
		sg := c.sendq.dequeue() // 發送者隊列
		if sg == nil { // 隊列爲空
			break
		}
		sg.elem = nil // 直接清空內存?這麼做不會內存溢出?
		if sg.releasetime != 0 {
			sg.releasetime = cputicks()
		}
		gp := sg.g
		gp.param = nil // 參數清零
		if raceenabled {
			raceacquireg(gp, c.raceaddr())
		}
		glist.push(gp) // gp入隊頭
	}
	unlock(&c.lock)

	// Ready all Gs now that we've dropped the channel lock.
	// 現在我們已經釋放了通道鎖,準備好所有G。
	for !glist.empty() { // 對所有的g,將schedlink清0,並且設置已經已經準備好
		gp := glist.pop()
		gp.schedlink = 0
		goready(gp, 3)
	}
}

// entry points for <- c from compiled code
//go:nosplit
/**
 * 編譯代碼中 <-c 的入口點
 * @param
 * @return
 **/
func chanrecv1(c *hchan, elem unsafe.Pointer) {
	chanrecv(c, elem, true)
}

//go:nosplit
/**
 *
 * @param
 * @return received true:表示已經接收到
 **/
func chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {
	_, received = chanrecv(c, elem, true)
	return
}

// chanrecv receives on channel c and writes the received data to ep.
// ep may be nil, in which case received data is ignored.
// If block == false and no elements are available, returns (false, false).
// Otherwise, if c is closed, zeros *ep and returns (true, false).
// Otherwise, fills in *ep with an element and returns (true, true).
// A non-nil ep must point to the heap or the caller's stack.
/**
 * chanrecv在通道c上接收並將接收到的數據寫入ep。
 * ep可能爲nil,在這種情況下,接收到的數據將被忽略。
 * 如果block == false並且沒有可用元素,則返回(false,false)。
 * 否則,如果c關閉,則* ep爲零並返回(true,false)。
 * 否則,用一個元素填充* ep並返回(true,true)。
 * 非nil必須指向堆或調用者的堆棧。
 * @param c 通道對象
 * @param ep 用於接收數據的指針
 * @param block true: 表示阻塞
 * @return selected true表示被選擇
 * @return received true表示已經接收到值
 **/
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
	// raceenabled: don't need to check ep, as it is always on the stack
	// or is new memory allocated by reflect.
	// raceenabled:不需要檢查ep,因爲它始終在堆棧中,或者是反射所分配的新內存。

	if debugChan { // 此值已經爲false
		print("chanrecv: chan=", c, "\n")
	}

	if c == nil { // 通道爲空
		if !block { // 非阻塞
			return
		}
		// 阻塞狀態
        // 將當前goroutine置於等待狀態並調用unlockf。
		gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2)
		throw("unreachable")
	}

	// Fast path: check for failed non-blocking operation without acquiring the lock.
	//
	// After observing that the channel is not ready for receiving, we observe that the
	// channel is not closed. Each of these observations is a single word-sized read
	// (first c.sendq.first or c.qcount, and second c.closed).
	// Because a channel cannot be reopened, the later observation of the channel
	// being not closed implies that it was also not closed at the moment of the
	// first observation. We behave as if we observed the channel at that moment
	// and report that the receive cannot proceed.
	//
	// The order of operations is important here: reversing the operations can lead to
	// incorrect behavior when racing with a close.
	// 快速路徑:不獲取鎖定而檢查失敗的非阻塞操作。
    //
    // 在觀察到通道尚未準備好接收之後,我們觀察到通道未關閉。 這些觀察中的每一個都是單個字(word)大小的讀取
    // (第一個c.sendq.first或c.qcount,第二個c.closed)。
    // 由於無法重新打開通道,因此對通道未關閉的後續觀察意味着它在第一次觀察時也未關閉。
    // 我們的行爲就好像我們當時在觀察該通道,並報告接收無法繼續進行。
    //
    // 操作順序在這裏很重要:在進行搶佔關閉時,反轉操作可能導致錯誤的行爲。
    // !block : 非阻塞
    // c.dataqsiz == 0 && c.sendq.first == nil : 通道沒有空間,並且沒有發送者
    // c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0 :  通道中有空間,並且沒有數據
    // atomic.Load(&c.closed) : 通道未關閉
	if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||
		c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0) &&
		atomic.Load(&c.closed) == 0 {
		return
	}

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

	lock(&c.lock) // 加鎖

	if c.closed != 0 && c.qcount == 0 { // 通道已經關閉,並且通道中沒有數據
		if raceenabled {
			raceacquire(c.raceaddr())
		}
		unlock(&c.lock) // 解鎖
		if ep != nil {
			typedmemclr(c.elemtype, ep) // 進行內存清理
		}
		return true, false
	}

	if sg := c.sendq.dequeue(); sg != nil { // 發送者隊例不爲空
		// Found a waiting sender. If buffer is size 0, receive value
		// directly from sender. Otherwise, receive from head of queue
		// and add sender's value to the tail of the queue (both map to
		// the same buffer slot because the queue is full).
		// 找到了等待發送者。 如果緩衝區的大小爲0,則直接從發送方接收值。
		// 否則,從隊列的開頭接收並將發件人的值添加到隊列的末尾(由於隊列已滿,因此兩者都映射到同一緩衝區)。
		recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
		return true, true
	}

    // 沒有發送者,數據隊列不爲空
	if c.qcount > 0 {
		// Receive directly from queue
		// 直接從數據隊列中接收數據
		qp := chanbuf(c, c.recvx)
		if raceenabled {raceacquire(qp)
			racerelease(qp)
		}
		if ep != nil { // 進行內存數據移動
			typedmemmove(c.elemtype, ep, qp)
		}
		typedmemclr(c.elemtype, qp) // 清理qp的c.elemtype類型內存數據
		c.recvx++ // 指向下一個接收位置
		if c.recvx == c.dataqsiz { // 說明已經指向了隊列末尾了的下一個位置了
			c.recvx = 0 // 重新指向頭部
		}
		c.qcount-- // 數據隊列中的數據減少一個
		unlock(&c.lock) // 解鎖
		return true, true
	}

    // 沒有發送者,並且隊列爲空,並且是非阻塞狀態
	if !block {
		unlock(&c.lock)
		return false, false
	}

	// no sender available: block on this channel.
	// 沒有可用的發送者:在此通道阻塞。
	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.
	// 在分配elem和將mysg入隊到gp.waitcopy可以找到它的地方之間沒有堆棧拆分。
	mysg.elem = ep
	mysg.waitlink = nil
	gp.waiting = mysg
	mysg.g = gp
	mysg.isSelect = false
	mysg.c = c
	gp.param = nil
	c.recvq.enqueue(mysg)
	// 阻塞狀態
    // 將當前goroutine置於等待狀態並調用unlockf。
	gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2)

	// someone woke us up
	// 有人把我們喚醒了
	if mysg != gp.waiting {
		throw("G waiting list is corrupted")
	}
	gp.waiting = nil
	gp.activeStackChans = false
	if mysg.releasetime > 0 {
		blockevent(mysg.releasetime-t0, 2)
	}
	closed := gp.param == nil
	gp.param = nil
	mysg.c = nil
	releaseSudog(mysg) // 釋放sudog
	return true, !closed
}

// recv processes a receive operation on a full channel c.
// There are 2 parts:
// 1) The value sent by the sender sg is put into the channel
//    and the sender is woken up to go on its merry way.
// 2) The value received by the receiver (the current G) is
//    written to ep.
// For synchronous channels, both values are the same.
// For asynchronous channels, the receiver gets its data from
// the channel buffer and the sender's data is put in the
// channel buffer.
// Channel c must be full and locked. recv unlocks c with unlockf.
// sg must already be dequeued from c.
// A non-nil ep must point to the heap or the caller's stack.
/**
 * recv在完整通道c上處理接收操作。
 * 有2個部分:
 *      1)將發送方sg發送的值放入通道中,並喚醒發送方以繼續進行。
 *      2)接收方接收到的值(當前G)被寫入ep。
 * 對於同步通道,兩個值相同。
 * 對於異步通道,接收者從通道緩衝區獲取數據,而發送者的數據放入通道緩衝區。
 * 頻道c必須已滿且已鎖定。 recv用unlockf解鎖c。
 * sg必須已經從c中出隊。
 * 非nil必須指向堆或調用者的堆棧。
 * @param c 通道對象針對
 * @param sg
 * @param ep 用戶接收元素的指針
 * @param unlockf 解鎖函數
 * @param skip
 * @return
 **/
func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
	if c.dataqsiz == 0 { // 數據隊列的大小是0,表示這上一個無緩衝的通道
		if raceenabled {
			racesync(c, sg)
		}
		if ep != nil { // 元素指針不爲空
			// copy data from sender
			// 直接從發送發拷貝數據
			recvDirect(c.elemtype, sg, ep)
		}
	} else { // 有緩衝通道
		// Queue is full. Take the item at the
		// head of the queue. Make the sender enqueue
		// its item at the tail of the queue. Since the
		// queue is full, those are both the same slot.
		// 隊列已滿。 將item放在隊列的開頭。 使發送者將其item排入隊列的末尾。
		// 由於隊列已滿,因此它們都是相同的槽位。
		qp := chanbuf(c, c.recvx)
		if raceenabled { // 此值已經爲false
			raceacquire(qp)
			racerelease(qp)
			raceacquireg(sg.g, qp)
			racereleaseg(sg.g, qp)
		}
		// copy data from queue to receiver
		if ep != nil {
			typedmemmove(c.elemtype, ep, qp)
		}
		// copy data from sender to queue
		// 將數據從隊列複製到接收者
		typedmemmove(c.elemtype, qp, sg.elem)
		c.recvx++ // 指向下一個接收位置
		if c.recvx == c.dataqsiz { // 已經達到了末尾的下一個位置,需要重新指向頭部
			c.recvx = 0
		}
		c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz // 發送者位置前移
	}
	sg.elem = nil
	gp := sg.g
	unlockf() // 解鎖
	gp.param = unsafe.Pointer(sg)
	if sg.releasetime != 0 {
		sg.releasetime = cputicks()
	}
	// 將當前goroutine置於等待狀態並解鎖。 可以通過調用goready(gp)使goroutine重新運行
	goready(gp, skip+1) // 標記go
}

/**
 * 
 * @param 
 * @return 
 **/
func chanparkcommit(gp *g, chanLock unsafe.Pointer) bool {
	// There are unlocked sudogs that point into gp's stack. Stack
	// copying must lock the channels of those sudogs.
	// 有未鎖定的sudog指向gp的堆棧。堆棧複製必須鎖定那些sudog的通道。
	// activeStackChans指示存在指向該goroutine堆棧的未鎖定通道。 
	// 如果爲true,則堆棧複製需要獲取通道鎖以保護堆棧的這些區域。
	gp.activeStackChans = true
	unlock((*mutex)(chanLock)) // 解鎖
	return true
}

// compiler implements
//
// select {
// case c <- v:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if selectnbsend(c, v) {
//		... foo
//	} else {
//		... bar
//	}
//
/**
 * 編譯器實現,將goroutine的select send(case c <- v)語句轉成對應的方法執行
 * @param
 * @return
 **/
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
	return chansend(c, elem, false, getcallerpc())
}

// compiler implements
//
//	select {
//	case v = <-c:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if selectnbrecv(&v, c) {
//		... foo
//	} else {
//		... bar
//	}
//
/**
 * 編譯器實現,將goroutine的select receive(v = <-c)語句轉成對應的方法執行
 * @param
 * @return
 **/
func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected bool) {
	selected, _ = chanrecv(c, elem, false)
	return
}

// compiler implements
//
//	select {
//	case v, ok = <-c:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if c != nil && selectnbrecv2(&v, &ok, c) {
//		... foo
//	} else {
//		... bar
//	}
//
/**
 * 編譯器實現,將goroutine的select receive(case v, ok = <-c:)語句轉成對應的方法執行
 * @param
 * @return
 **/
func selectnbrecv2(elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
	// TODO(khr): just return 2 values from this function, now that it is in Go.
	// TODO(khr):此函數位於Go中,只需返回2個值即可。表示這是一個歷史遺留
	selected, *received = chanrecv(c, elem, false)
	return
}

//go:linkname reflect_chansend reflect.chansend
/**
 *
 * go:linkname引導編譯器將當前(私有)方法或者變量在編譯時鏈接到指定的位置的方法或者變量,
 * 第一個參數表示當前方法或變量,第二個參數表示目標方法或變量,因爲這關指令會破壞系統和包的模塊化,
 * 因此在使用時必須導入unsafe
 * 參見:https://blog.csdn.net/lastsweetop/article/details/78830772
 * @param
 * @return
 **/
func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
	return chansend(c, elem, !nb, getcallerpc())
}

//go:linkname reflect_chanrecv reflect.chanrecv
func reflect_chanrecv(c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
	return chanrecv(c, elem, !nb)
}

//go:linkname reflect_chanlen reflect.chanlen
func reflect_chanlen(c *hchan) int {
	if c == nil {
		return 0
	}
	return int(c.qcount)
}

//go:linkname reflectlite_chanlen internal/reflectlite.chanlen
func reflectlite_chanlen(c *hchan) int {
	if c == nil {
		return 0
	}
	return int(c.qcount)
}

//go:linkname reflect_chancap reflect.chancap
func reflect_chancap(c *hchan) int {
	if c == nil {
		return 0
	}
	return int(c.dataqsiz)
}

//go:linkname reflect_chanclose reflect.chanclose
func reflect_chanclose(c *hchan) {
	closechan(c)
}

/**
 * 入隊操作
 * @param sudog 需要入隊的元素
 * @return
 **/
func (q *waitq) enqueue(sgp *sudog) {
	sgp.next = nil
	x := q.last
	if x == nil { // 隊列中沒有元素
		sgp.prev = nil
		q.first = sgp
		q.last = sgp
		return
	}

	// 隊列中已經有元素
	sgp.prev = x
	x.next = sgp
	q.last = sgp
}

/**
 * 出隊
 * @param
 * @return
 **/
func (q *waitq) dequeue() *sudog {
	for {
		sgp := q.first
		if sgp == nil { // 隊列中沒有元素
			return nil
		}
		y := sgp.next
		if y == nil { // 隊列中只有一個元素
			q.first = nil
			q.last = nil
		} else {
			y.prev = nil
			q.first = y
			sgp.next = nil // mark as removed (see dequeueSudog) // 標記爲已刪除(請參閱dequeueSudog)
		}

		// if a goroutine was put on this queue because of a
		// select, there is a small window between the goroutine
		// being woken up by a different case and it grabbing the
		// channel locks. Once it has the lock
		// it removes itself from the queue, so we won't see it after that.
		// We use a flag in the G struct to tell us when someone
		// else has won the race to signal this goroutine but the goroutine
		// hasn't removed itself from the queue yet.
		// 如果由於選擇而將goroutine放在此隊列中,則在其他情況下喚醒goroutine並獲取通道鎖之間會有一個小窗口。
		// 一旦擁有了鎖,它就會將自己從隊列中刪除,因此之後我們將看不到它。
        // 我們在G結構中使用一個標誌來告訴我們何時其他人贏得了發信號通知此goroutine的競賽,
        // 但goroutine尚未將自己從隊列中刪除。
		if sgp.isSelect && !atomic.Cas(&sgp.g.selectDone, 0, 1) {
			continue
		}

		return sgp
	}
}

func (c *hchan) raceaddr() unsafe.Pointer {
	// Treat read-like and write-like operations on the channel to
	// happen at this address. Avoid using the address of qcount
	// or dataqsiz, because the len() and cap() builtins read
	// those addresses, and we don't want them racing with
	// operations like close().
	// 將通道上的讀取和寫入操作視爲在此地址發生。 避免使用qcount或dataqsiz的地址,
	// 因爲內置的len()和cap()會讀取這些地址,並且我們不希望它們與close()之類的操作競爭。
	return unsafe.Pointer(&c.buf)
}

/**
 * 這個方法現在看來沒有什麼用了
 * @param
 * @return
 **/
func racesync(c *hchan, sg *sudog) {
	racerelease(chanbuf(c, 0))
	raceacquireg(sg.g, chanbuf(c, 0))
	racereleaseg(sg.g, chanbuf(c, 0))
	raceacquire(chanbuf(c, 0))
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章