Channel data structure

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed. Channel is a major feature of the Go language, using atomic functions or mutexes is not as simple as using the channel, and the channel in the Go language can be returned as a function parameter pass and return value, which is synchronized between goroutine by sending and receiving data ( When learning and using the go language, we should keep in mind that all the structures in the go language are copies of values.

This article does not explain the use of channel, directly on acid (DAI) dishes (MA):

type hchan struct {qcount   uint           //队列数据总的数据数量dataqsiz uint           //环形队列的数据大小 buf      unsafe.Pointer //指向dataqsiz元素类型大小的数组elemsize uint16closed   uint32elemtype *_type // 元素类型sendx    uint   // 发送数据时的游标recvx    uint   // 接收数据时的游标recvq    waitq  // 接收而阻塞的等待队列sendq    waitq  // 发送而阻塞的等待队列        lock mutex      // 保护hchan所有字段的锁}
Hchan is the structure of Chan, in the Hchan structure Qcount and Elemsize specify the capacity and usage of the queue, dataqsiz the size of the queue, the entire Hchan structure only records the queue size-related values, with the buffer of Chan need make the time specified, Let's take a quick look at how Chan's Make method allocates buffers
func makechan(t *chantype, size int64) *hchan {elem := t.elem        ...        ...var c *hchanif elem.kind&kindNoPointers != 0 || size == 0 {c = (*hchan)(mallocgc(hchanSize+uintptr(size)*elem.size, nil, true))if size > 0 && elem.size != 0 {c.buf = add(unsafe.Pointer(c), hchanSize)} else {c.buf = unsafe.Pointer(c)}} else {c = new(hchan)c.buf = newarray(elem, int(size))}...        ...}

Makechan initializes the Hchan with a value of 0 and then determines if the size is a buffer, and Chan then assigns an array of type "_type" of size to the Hchan struct.

type waitq struct {first *sudoglast  *sudog}type sudog struct {g          *gselectdone *uint32 next       *sudogprev       *sudogelem       unsafe.Pointer acquiretime int64releasetime int64ticket      uint32waitlink    *sudog // g.waiting listc           *hchan // channel}

G and Elem store goroutine data separately

Send Channel

When writing data to the channel, the corresponding method in the runtime package is as follows:

Func chansend (t *chantype, C *hchan, ep unsafe. Pointer, block bool, CALLERPC uintptr) bool {if raceenabled {Racereadobjectpc (T.elem, EP, Callerpc, Funcpc (chansend))}if m sanenabled {msanread (EP, t.elem.size)}if c = = Nil {if!block {return False}gopark (nil, nil, "Chan Send (Nil chan)", Tracee Vgostop, 2) throw ("unreachable")}if Debugchan {print ("chansend:chan=", C, "\ n")}if raceenabled {racereadpc (unsafe. Pointer (c), Callerpc, FUNCPC (chansend))}if!block && c.closed = = 0 && ((c.dataqsiz = 0 && c.recvq . First = nil) | | (C.dataqsiz > 0 && c.qcount = = C.dataqsiz)) {return False}var t0 int64if blockprofilerate > 0 {t0 = cputicks ()}lock (&c.lock) if c.closed! = 0 {unlock (&c.loc k) Panic (Plainerror ("Send on Closed channel")}if sg: = C.recvq.dequeue (); SG! = nil {send (c, SG, EP, func () {Unlock (&c.lock)}) return True}if C.qcount < C.dataqsiz {QP: = Chanbuf (c, c.send X) 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}GP: = GETG () Mysg: = Acquiresudog () Mysg.releasetime = 0if T0! = 0 {mysg.releasetime = -1}mysg.elem = Epmysg.wa Itlink = NILMYSG.G = Gpmysg.selectdone = nilmysg.c = cgp.waiting = Mysggp.param = Nilc.sendq.enqueue (mysg) Goparkunlock (&am P;c.lock, "Chan send", Traceevgoblocksend, 3) if mysg! = gp.waiting {throw ("G waiting list is corrupted")}gp.waiting = Nili F Gp.param = = Nil {if c.closed = = 0 {throw ("chansend:spurious wakeup")}panic (Plainerror ("Send on Closed channel")}gp.par am = Nilif mysg.releasetime > 0 {blockevent (mysg.releasetime-t0, 2)}mysg.c = Nilreleasesudog (MYSG) return True}

When sending data, determine the type of channel, if there is a buffer, determine if the channel has space, and then from the waiting channel to obtain the receiver in the channel, if the receiver, the object is passed directly to the receiver, Then the receiver's go into the run G queue where p is located, the sending process is completed, if not taken to the receiver, then the sender enqueue to send the channel, the sender into the blocking state, the buffer channel needs to first determine whether the channel buffer has space, If the buffer space is full, the sender is enqueue to send the channel, the sender enters the blocking state if the buffer space is not full, then the element is copied to the buffer, then the sender will not enter the blocking state, and finally try to wake up a recipient in the waiting queue.



Receive channel

When receiving data to the channel, the corresponding method in the runtime package is as follows:

Func chanrecv (t *chantype, C *hchan, ep unsafe. Pointer, block bool) (selected, received bool) {//Raceenabled:don ' t need to check EP, as it's 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 (n Il, nil, "Chan receive (Nil Chan)", Traceevgostop, 2) throw ("unreachable")}//Fast Path:check for failed non-blocking oper ation without acquiring the lock.////after observing then the channel is not a ready for receiving, we observe that the//C Hannel is not closed.  Each of these observations are 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 isn't closed implies that it is also not closed At the moment of the//first observation. We behave as if we observed the channel at this moment//and report that the receive cannot proceed.////the order of Oper Ations is important here:reversinG The operations can leads to//incorrect behavior when racing with a close.if!block && (C.dataqsiz = = 0 &&amp ; C.sendq.first = = Nil | | C.dataqsiz > 0 && Atomic. Loaduint (&c.qcount) = = 0) &&atomic.  Load (&c.closed) = = 0 {return}var t0 int64if blockprofilerate > 0 {t0 = cputicks ()}lock (&c.lock) if c.closed! = 0 && C.qcount = = 0 {if raceenabled {raceacquire (unsafe. Pointer (c))}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, the 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 slo t because the queue is full). recv (c, SG, EP, func () {Unlock (&c.lock)}) return True, True}if c.qcount > 0 {//recei ve directly from QUEUEQP: = 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: = ge TG () Mysg: = Acquiresudog () Mysg.releasetime = 0if T0! = 0 {mysg.releasetime = -1}//No stack splits between assigning elem and enqueuing mysg//on gp.waiting where copystack can find It.mysg.elem = Epmysg.waitlink = nilgp.waiting = MYSGMYSG.G = Gpmysg.selectdone = nilmysg.c = Cgp.param = Nilc.recvq.enqueue (mysg) goparkunlock (&c.lock, "Chan receive", TRACEEVGOBLOCKRECV, 3)//Someone woke us upif mysg! = gp.waiting {throw ("G waiting list is corrupted")}gp.waiting = Nilif Mysg.releasetime > 0 {blockevent (mysg.releasetime-t0, 2)}closed: = Gp.param = Nilgp.param = NILMYSG.C = NilreleaseSudo G (MYSG) return True,!closed}

Receive channel and send similar to first also determine the type of channel, and then if there is a buffer channel to determine whether there are elements in the buffer, then from the channel to obtain the recipient, if fetched, then directly from the receiver to obtain the element, and wake the sender, the reception process is complete, If the receiver is not fetched, blocking the current goroutine and waiting for the sender to wake up, if it is a buffered channel needs to determine whether there are elements in the buffer, buffering is empty, blocking the current goroutine and waiting for the sender to wake up, buffer if not empty, then take out the first element in the buffer , and then try to wake up a sender in the channel (this article is temporarily a temporary version, some things need to be considered and will be updated soon ...). )

Next I will publish the structure of select to say a preview first ...

select {       case c <- v:              ... foo       default:              ... bar}//select的case和default 编译器最终会编译成if elseif selectnbsend(c, v) {       ... foo} else {       ... bar}

I will complete the specific implementation of select in the next few days ...

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.