我可能並不會使用golang chan

package main

import (
    "fmt"
)

func main(){
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
}

複製代碼

有人問我,上面的程序能不能正常運行?golang

我當時就生氣了,感受本身的智商受到了極大的侮辱。他讓我冷靜冷靜,分析分析爲啥?數組

我話很少說,上面先來一頓操做緩存

0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	PCDATA	$0, $0
	0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	LEAQ	type.chan int(SB), AX
	0x002b 00043 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	PCDATA	$2, $0
	0x002b 00043 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	MOVQ	AX, (SP)
	0x002f 00047 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	MOVQ	$0, 8(SP)
	0x0038 00056 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	CALL	runtime.makechan(SB)
	0x003d 00061 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	PCDATA	$2, $1
	0x003d 00061 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	MOVQ	16(SP), AX
	0x0042 00066 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	PCDATA	$0, $1
	0x0042 00066 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:8)	MOVQ	AX, "".ch+56(SP)
複製代碼

咱們發現,建立chan實際上調用的是runtime.makechan。該函數提供了兩個參數,第一個參數是type.chan int類型, 第二個參數是0,也就是說咱們在make的時候並未提供第二個參數。返回值是一個hchan類型指針。以後對ch進行了賦值操做。 先看一眼hchan是個啥,混個眼熟bash

type hchan struct {
	qcount   uint           // 隊列中的已存數據長度
	dataqsiz uint           // 循環隊列的大小
	buf      unsafe.Pointer // 指向循環隊列數組
	elemsize uint16         // 元素大小
	closed   uint32         // 當前channel是否關閉
	elemtype *_type // 元素類型
	sendx    uint   // 發送索引
	recvx    uint   // 接受索引
	recvq    waitq  // 等待接受的接受者的鏈表
	sendq    waitq  // 等待發送的發送者鏈表

	// 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. lock mutex } 複製代碼

看一下該函數的原型ide

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")
	}
	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
		throw("makechan: bad alignment")
	}

	mem, overflow := math.MulUintptr(elem.size, uintptr(size))
	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.
	var c *hchan
	switch {
	case mem == 0:
		// Queue or element size is zero.
		c = (*hchan)(mallocgc(hchanSize, nil, true))
		// Race detector uses this location for synchronization.
		c.buf = c.raceaddr()
	case elem.kind&kindNoPointers != 0:
		// Elements do not contain pointers.
		// Allocate hchan and buf in one call.
		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, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n")
	}
	return c
}
複製代碼

由於咱們make時,提供的size大小爲0,因此mem==0,hchandataqsiz也是0.在對ch變量賦值時,賦得是hchan的指針。函數

ch <- 1 這一步是向chan中寫數據,該動做調用的是runtime.chansend1oop

0x0047 00071 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	PCDATA	$2, $0
	0x0047 00071 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	MOVQ	AX, (SP)
	0x004b 00075 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	PCDATA	$2, $1
	0x004b 00075 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	LEAQ	"".statictmp_0(SB), AX
	0x0052 00082 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	PCDATA	$2, $0
	0x0052 00082 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	MOVQ	AX, 8(SP)
	0x0057 00087 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:11)	CALL	runtime.chansend1(SB)
複製代碼

看一下"".statictmp_0這個東西是什麼,post

"".statictmp_0 SRODATA size=8
	0x0000 01 00 00 00 00 00 00 00                          ........
複製代碼

若是咱們往chan中寫的是11,這個東西的值又是什麼ui

"".statictmp_0 SRODATA size=8
	0x0000 0b 00 00 00 00 00 00 00                          ........
複製代碼

就是一個十六進制表示的數字,繼續看this

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.
 */
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) goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3) // 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
	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
}
複製代碼

第一個參數是上面建立的*hchan,第二個參數是"".statictmp_0(SB)地址。由於咱們建立的是不帶緩存的chan,其qcount和dataqsiz都是0,在對chan加鎖後lock(&c.lock),阻塞在channel上,等待接受者能夠幫咱們完成任務,由於當前代碼無接受者goroutine,致使死鎖問題。

再看來一個示例

package main

import (
    "fmt"
)

func main(){
    var ch chan int
    ch <- 11
    fmt.Println(<-ch)
}

複製代碼

看一下彙編後的狀況

0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:10)	PCDATA	$0, $1
	0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:10)	MOVQ	$0, "".ch+56(SP)
	0x002d 00045 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	MOVQ	$0, (SP)
	0x0035 00053 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	PCDATA	$2, $1
	0x0035 00053 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	LEAQ	"".statictmp_0(SB), AX
	0x003c 00060 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	PCDATA	$2, $0
	0x003c 00060 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	MOVQ	AX, 8(SP)
	0x0041 00065 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12)	CALL	`runtime.chansend1`(SB)
複製代碼

runtime.chansend1函數的第一個參數賦個0.

該實例運行也是異常的,異常的緣由都是死鎖,只不過

➜  channeltest go run demo1.go
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
	/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:12 +0x3a
exit status 2
複製代碼

此次死鎖的緣由是,向nil channel發送數據,也就是說,var聲明的ch是一個nil chanl,在chansend方法中

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")
	}
	...
}
複製代碼

檢查到c==nil,以後,會將當前goroutine,gopark掉。park掉的緣由是waitReasonChanSendNilChan

const (
    waitReasonChanSendNilChan                         // "chan send (nil chan)"
)
複製代碼

要想讓上面的示例,成功運行,該怎麼辦呢?

func main(){
    // ch := make(chan int)
    ch:= make(chan int, 1)
    // var ch chan int
    ch <- 11
    fmt.Println(<-ch)  //11
}
複製代碼

加上chan的緩存。上面的示例就能正常運行了,很穩,爲啥呢?仔細看看chansend

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
	}
複製代碼

上述代碼節選自runtime.chansend函數,當咱們首次將元素放到chan裏面的時候,由於c.dataqsiz=1,c.qcount=0. qp := chanbuf(c, c.sendx)指向c.buf的第c.sendx個槽。typedmemmove(c.elemtype, qp, ep)該函數將ep的值複製到qp.以後c.sendx++將發送索引加一。若是發送索引達到了循環隊列的最大長度,則從頭開始。

c的數據量自增1.解鎖在chansend中加的鎖,並返回true. 繼續往下走

0x0065 00101 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	PCDATA	$0, $0
	0x0065 00101 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	MOVQ	"".ch+56(SP), AX
	0x006a 00106 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	PCDATA	$2, $0
	0x006a 00106 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	MOVQ	AX, (SP)
	0x006e 00110 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	PCDATA	$2, $1
	0x006e 00110 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	LEAQ	""..autotmp_2+48(SP), AX
	0x0073 00115 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	PCDATA	$2, $0
	0x0073 00115 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	MOVQ	AX, 8(SP)
	0x0078 00120 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	CALL	runtime.chanrecv1(SB)
	0x007d 00125 (/Users/zhaojunwei/workspace/src/just.for.test/channeltest/demo1.go:13)	MOVQ	""..autotmp_2+48(SP), AX
複製代碼

下面的接受數據<-ch,涉及的函數調用是runtime.chanrecv1,其參數爲第一個參數是ch,第二個參數是須要存放讀取出來的數據的寄存器地址""..autotmp_2+48(SP)。沒有返回值。由於咱們並無申明變量來接受數據。

// entry points for <- c from compiled code
//go:nosplit
func chanrecv1(c *hchan, elem unsafe.Pointer) {
	chanrecv(c, elem, true)
}

// chanrecv 從channel中接受數據並將接受的數據寫入到ep.
// ep可能爲nil,這種狀況下接受的數據將被忽略。
// 若是block == false而且沒有元素可使用,則返回(false, false)
// 不然,若是c已經關閉,零值的*ep並返回(true, false)
// 不然,使用元素填充ep並返回(true, true)
// 非空的ep必須指向堆或者調用者棧
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. if debugChan { print("chanrecv: chan=", c, "\n") } if c == nil { if !block { return } 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. 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).
		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)
		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.
	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)
	goparkunlock(&c.lock, waitReasonChanReceive, traceEvGoBlockRecv, 3)

	// someone woke us up
	if mysg != gp.waiting {
		throw("G waiting list is corrupted")
	}
	gp.waiting = nil
	if mysg.releasetime > 0 {
		blockevent(mysg.releasetime-t0, 2)
	}
	closed := gp.param == nil
	gp.param = nil
	mysg.c = nil
	releaseSudog(mysg)
	return true, !closed
}
複製代碼

由於c.qcount>0,直接從隊列中接受值,整個流程結束,其實上述的示例是比較簡單的。更復雜一些的應用場景有哪些呢?

  • 多個goroutine的同步控制
  • 結合select使用
  • 任務隊列

關於第一個應用場景示例

import (
	"time"
)
func worker(done chan bool) {
	time.Sleep(time.Second)
	done <- true
}
func main() {
	done := make(chan bool, 1)
	go worker(done)
	<-done
}
複製代碼

只有在工做goroutine完成了任務處理以後,整個程序才能順利結束.

第二個應用場景示例

package main

import (
	"fmt"
)
func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("fibonacci quit")
			return
		}
	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}
複製代碼

第三個應用場景示例

package main

import (
	"fmt"
)
func main() {
	done := make(chan int, 10) // 帶 10 個緩存

	// 開N個後臺打印線程
	for i := 0; i < cap(done); i++ {
		go func(){
			fmt.Println("你好, 世界")
			done <- 1
		}()
	}

	// 等待N個後臺線程完成
	for i := 0; i < cap(done); i++ {
		<-done
	}
}
複製代碼

固然了,上述的示例,有更方便的替換方案實現:

package main

import (
	"fmt"
	"sync"
)
func main() {
	var wg sync.WaitGroup

	// 開N個後臺打印線程
	for i := 0; i < 10; i++ {
		wg.Add(1)

		go func() {
			fmt.Println("你好, 世界")
			wg.Done()
		}()
	}

	// 等待N個後臺線程完成
	wg.Wait()
}
複製代碼

有關關閉channel的原則:

  • 一個 sender,多個 receiver,由 sender 來關閉 channel,通知數據已發送完畢。

  • 一旦 sender 有多個,可能就沒法判斷數據是否完畢了。這時候能夠藉助外部額外 channel 來作信號廣播。這種作法相似於 done channel,或者 stop channel。

  • 若是肯定不會有 goroutine 在通訊過程當中被阻塞,也能夠不關閉 channel,等待 GC 對其進行回收。

之因此出現類限制,主要是由於channel類型沒辦法屢次關閉,在關閉的chan上調用close將panic,向close的chan發送數據也將panic.從close的chan上讀取數據,將獲取chan元素類型的零值,此種場景下要作第二個返回值的判斷,以防止沒法確切的知道究竟是chan關閉了,仍是從chan中讀取的數就是零值。

至於有關chan做爲參數時,是值傳遞仍是引用傳遞,咱們能夠看一個小示例:

package main

import (
	"fmt"
)

func worker(stop chan bool) {
	
	for i:=0;i<10;i++ {
		fmt.Println("幹活....")
	}
	stop <- true
}


func main() {	
	stop := make(chan bool)
	go worker(stop)
	<- stop
}
複製代碼

檢點看一下彙編

0x001d 00029 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	PCDATA	$0, $0
	0x001d 00029 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	LEAQ	type.chan bool(SB), AX
	0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	PCDATA	$2, $0
	0x0024 00036 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	MOVQ	AX, (SP)
	0x0028 00040 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	MOVQ	$0, 8(SP)
	0x0031 00049 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	CALL	runtime.makechan(SB)
	0x0036 00054 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	PCDATA	$2, $1
	0x0036 00054 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	MOVQ	16(SP), AX
	0x003b 00059 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	PCDATA	$0, $1
	0x003b 00059 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:17)	MOVQ	AX, "".stop+24(SP)
	0x0040 00064 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	MOVL	$8, (SP)
	0x0047 00071 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	PCDATA	$2, $2
	0x0047 00071 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	LEAQ	"".worker·f(SB), CX
	0x004e 00078 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	PCDATA	$2, $1
	0x004e 00078 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	MOVQ	CX, 8(SP)
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	PCDATA	$2, $0
	0x0053 00083 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	MOVQ	AX, 16(SP)
	0x0058 00088 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:18)	CALL	runtime.newproc(SB)
	0x005d 00093 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	PCDATA	$2, $1
	0x005d 00093 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	PCDATA	$0, $0
	0x005d 00093 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	MOVQ	"".stop+24(SP), AX
	0x0062 00098 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	PCDATA	$2, $0
	0x0062 00098 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	MOVQ	AX, (SP)
	0x0066 00102 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	MOVQ	$0, 8(SP)
	0x006f 00111 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:19)	CALL	runtime.chanrecv1(SB)
	0x0074 00116 (/Users/zhaojunwei/workspace/src/just.for.test/goroutinetest/main.go:20)	MOVQ	32(SP), BP
複製代碼

咱們在新起一個goroutine進行執行業務處理是,使用runtime.newproc該函數有兩個參數,第一個參數是siz=8,第二個參數是一個函數地址

// 建立一個新的g運行fn,帶有size字節的參數。
// 將它放在g的等待隊列中。
// 編譯器將go語句轉化成對該方法的調用。
// 
// Cannot split the stack because it assumes that the arguments
// are available sequentially after &fn; they would not be
// copied if a stack split occurred.
//go:nosplit
func newproc(siz int32, fn *funcval) {
	argp := add(unsafe.Pointer(&fn), sys.PtrSize)
	gp := getg()
	pc := getcallerpc()
	systemstack(func() {
		newproc1(fn, (*uint8)(argp), siz, gp, pc)
	})
}

複製代碼

本例中第三個參數是函數的參數,也就是咱們makechan出來的*hchan,這也就是爲什麼,咱們能夠在業務函數中能夠修改chan的緣由。

本系列文章:

有任何問題,歡迎留言

相關文章
相關標籤/搜索