This is a creation in Article, where the information may have evolved or changed.
Chan is a FIFO queue, and Chan is divided into two types of synchronous and asynchronous
Synchronous Chan completes the process of transmitting elements between the sender and the recipient by hand, and must require the other person's presence to complete a send or receive
The asynchronous Chan send and receive is based on Chan's cache, but when the cache queue fills up, the sender enters the send queue, and when the cache queue is empty, the recipient accesses the waiting queue.
Chan's data structure:
structHchan{ uintgoqcount;// total data in the q uintgodataqsiz;// size of the circular q uint16elemsize; uint16pad;// ensures proper alignment of the buffer that follows Hchan in memory boolclosed; Alg*elemalg;// interface for element type uintgosendx;// send index uintgorecvx;// receive index WaitQrecvq;// list of recv waiters WaitQsendq;// list of send waiters Lock;};
Chan Send
Voidruntime chansend (Chantype *t, Hchan *c, byte *ep, bool *pres, void *pc) {Sudog *sg; Sudog Mysg; g* GP; Int64 t0; if (c = = nil) {used (t); if (pres! = nil) {*pres = false; Return } Runtime Park (nil, nil, "Chan Send (Nil chan)"); Return Not reached} if (Debug) {runtime printf ("chansend:chan=%p; Elem= ", c); C->elemalg->print (C->elemsize, EP); Runtime prints ("\ n"); } t0 = 0; Mysg.releasetime = 0; if (runtime blockprofilerate > 0) {t0 = runtime cputicks (); Mysg.releasetime =-1; } Runtime Lock (c); if (raceenabled) runtime racereadpc (c, PC, runtime Chansend); if (c->closed) goto closed; if (C->dataqsiz > 0) goto asynch; sg = dequeue (&C->RECVQ); if (sg! = nil) {if (raceenabled) Racesync (c, SG); runtime unlock (c); GP = sg->g; Gp->param = SG; if (Sg->elem! = nil) c->elemalg->copy (C->elemsize, Sg->elem, EP); if (SG->releasetime) Sg->releasetime = Runtime cputicks (); Runtime ready (GP); if (pres! = nil) *pres = true; Return } if (pres! = nil) {runtime unlock (c); *pres = false; Return } Mysg.elem = EP; MYSG.G = g; Mysg.selgen = Noselgen; G->param = nil; Enqueue (&C->SENDQ, &MYSG); Runtime Park (runtime unlock, C, "Chan send"); if (G->param = = nil) {Runtime lock (c); if (!c->closed) runtime throw ("Chansend:spurious wakeup"); Goto closed; } if (Mysg.releasetime > 0) Runtime blockevent (mysg.releasetime-t0, 2); Return Asynch:if (c->closed) goto closed; if (C->qcount >= c->dataqsiz) {if (pres! = nil) {runtime unlock (c); *pres = false; Return } MYSG.G = g; Mysg.elem = nil; Mysg.selgen = Noselgen; Enqueue (&C->SENDQ, &MYSG); Runtime Park (runtime unlock, C, "Chan send"); Runtime Lock (c); Goto Asynch; } if (raceenabled) Runtime Racerelease (Chanbuf (c, c->sendx)); C->elemalg->copy (C->elemsize, Chanbuf (c, C->sendx), EP); if (++c->sendx = = c->dataqsiz) c->sendx = 0; c->qcount++; sg = dequeue (&C->RECVQ); if (sg! = nil) {GP = sg->g; runtime unlock (c); if (sg->releasetime) sg->releasetime = Runtime cputicks (); Runtime ready (GP); } else Runtime unlock (c); if (pres! = nil) *pres = true; if (Mysg.releasetime > 0) Runtime blockevent (mysg.releasetime-t0, 2); Return Closed:runtime unlock (c); Runtime panicstring ("Send on closed channel");}
- To determine the queue type, the asynchronous queue goes to 5
- Get recipients in the wait queue from the wait queue
- If the recipient is taken, the object is passed directly to the recipient, then the recipient is awakened, the sending process is complete
- If the recipient is not taken, the sender is enqueue to the Send queue and the sender enters a blocking state
- Asynchronous queues first determine if the queue cache still has space
- If the cache space is full, the sender is enqueue to the Send queue and the sender enters a blocking state
- If the cache space is not full, the element is copied to the cache, and the sender does not enter a blocking state
- Attempt to wake up a recipient in the wait queue
Chan accepts
Voidruntime chanrecv (Chantype *t, hchan* C, byte *ep, bool *selected, bool *received) {Sudog *sg; Sudog Mysg; G *GP; Int64 t0; if (debug) Runtime printf ("Chanrecv:chan=%p\n", c); if (c = = nil) {used (t); if (Selected! = nil) {*selected = false; Return } Runtime Park (nil, Nil, "Chan receive (Nil Chan)"); Return Not reached} t0 = 0; Mysg.releasetime = 0; if (runtime blockprofilerate > 0) {t0 = runtime cputicks (); Mysg.releasetime =-1; } Runtime Lock (c); if (C->dataqsiz > 0) goto asynch; if (c->closed) goto closed; sg = dequeue (&C->SENDQ); if (sg! = nil) {if (raceenabled) Racesync (c, SG); runtime unlock (c); if (ep! = nil) c->elemalg->copy (C->elemsize, EP, Sg->elem); GP = sg->g; Gp->param = SG; if (sg->releasetime) sg->releasetime = Runtime cputicks (); Runtime ready (GP); if (Selected! = nil) *selected = true; if (received = nil) *received = true; Return } if (selected! = nil) {runtime unlock (c); *selected = false; Return } Mysg.elem = EP; MYSG.G = g; Mysg.selgen = Noselgen; G->param = nil; Enqueue (&C->RECVQ, &MYSG); Runtime Park (runtime unlock, C, "Chan receive"); if (G->param = = nil) {Runtime lock (c); if (!c->closed) runtime throw ("Chanrecv:spurious wakeup"); Goto closed; } if (Received! = nil) *received = true; if (Mysg.releasetime > 0) Runtime blockevent (mysg.releasetime-t0, 2); Return Asynch:if (c->qcount <= 0) {if (c->closed) goto closed; if (Selected! = nil) {runtime unlock (c); *selected = false; if (Received! = nil) *received = false; Return } MYSG.G = g; Mysg.elem = nil; Mysg.selgen = Noselgen; Enqueue (&C->RECVQ, &MYSG); Runtime Park (runtime unlock, C, "Chan receive"); Runtime Lock (c); Goto Asynch; } if (Raceenabled) Runtime Raceacquire (Chanbuf (c, C->RECVX)); if (ep! = nil) c->elemalg->copy (C->elemsize, EP, Chanbuf (C, C->RECVX)); C->elemalg->copy (C->elemsize, Chanbuf (c, C->RECVX), nil); if (++C->RECVX = = c->dataqsiz) C->RECVX = 0; c->qcount--; sg = dequeue (&C->SENDQ); if (sg! = nil) {GP = sg->g; runtime unlock (c); if (sg->releasetime) sg->releasetime = Runtime cputicks (); Runtime ready (GP); } else Runtime unlock (c); if (Selected! = nil) *selected = true; if (Received! = nil) *received = true; if (Mysg.releasetime > 0) Runtime blockevent (mysg.releasetime-t0, 2); Return Closed:if (ep = nil) C->elemalg->copy (C->elemsize, EP, nil); if (Selected! = nil) *selected = true; if (Received! = nil) *received = false; if (raceenabled) runtime Raceacquire (c); runtime unlock (c); if (Mysg.releasetime > 0) Runtime blockevent (mysg.releasetime -T0, 2);}
- determines the queue type, and if it is an asynchronous queue, go to 5
- get the recipient from the Send queue
- If the recipient is taken, the element is fetched directly from the recipient, and the sender is awakened, and the process is completed
- If the recipient is not taken, enqueue itself to the wait queue, blocking goroutine waiting for the sender to wake up
- Async Queue First determine if there are elements in the queue cache
- cache is empty, it enqueue itself to the waiting queue , blocking goroutine waiting for the sender to wake up
- cache non-empty, remove the first element in the cache
- then try to wake up a sender in the Send queue