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
,hchan
的dataqsiz
也是0.在對ch變量賦值時,賦得是hchan的指針。函數
ch <- 1
這一步是向chan中寫數據,該動做調用的是runtime.chansend1
oop
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,直接從隊列中接受值,整個流程結束,其實上述的示例是比較簡單的。更復雜一些的應用場景有哪些呢?
關於第一個應用場景示例
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的緣由。
本系列文章:
有任何問題,歡迎留言