回收类网站源码分享(回收网址)

很多朋友对于回收类网站源码分享和回收网址不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

概述

channel是golang最重要的一个结构,是区别于其他高级语言的最重要的特色之一,也是goroutine通信是必须要具备的要素之一。很多人用过它,但是很少有人彻底理解过它,甚至c<-x,<-c这样的语法可能都记不清晰,怎么办?本文教你从源码编译器的角度全方位的剖析channel的用法。

channel是什么

本质上就实现角度来讲,golang的channel就是一个环形队列(ringbuffer)的实现。我们称chan为管理结构,channel里面可以放任何类型的对象,我们称之为元素。

我们从channel的使用姿势入手,讲解最详细的channel使用方法。

channel数据结构

channel使用

阻塞式channel:

vara=make(chanint)

非阻塞channel:

vara=make(chanint,10)

阻塞和非阻塞关键就在是否有capacity。没有capacity的话,channel也就只是个同步通信工具。

向channel中发送内容:

ch:=make(chanint,100)\nch<-1

从channel中接收内容:

vari=<-ch

关闭channel:

close(ch)

注意,已关闭的channel,再次关闭会panic

close(ch)\nclose(ch)//panic:closeofclosedchannel

在channel关闭时自动退出循环

funcmain(){\nch:=make(chanint,100)\nforelem:=rangech{//主要就是这里的forrange…\nfmt.Println(i)\n}\n}

获取channel中元素数量、buffer容量:

funcmain(){\nch:=make(chanint,100)\nch<-1\nfmt.Println(len(ch))//1\nfmt.Println(cap(ch))//100\n}

注意,len和cap并不是函数调用。编译后是直接去取hchan的field了。

closedchannel

被关闭的channel不能再向其中发送内容,否则会panic

ch:=make(chanint)\nclose(ch)\nch<-1//panic:sendonclosedchannel

注意,如果closechannel时,有sendergoroutine挂在channel的阻塞发送队列中,会导致panic:

funcmain(){\nch:=make(chanint)\ngofunc(){ch<-1}()//panic:sendonclosedchannel\ntime.Sleep(time.Second)\ngofunc(){close(ch)}()\ntime.Sleep(time.Second)\nx,ok:=<-ch\nfmt.Println(x,ok)\n}

close一个channel会唤醒所有等待在该channel上的g,并使其进入Grunnable状态,这时这些writergoroutine会发现该channel已经是closed状态,就panic了。

在不确定是否还有goroutine需要向channel发送数据时,请勿贸然关闭channel。

可以从已经closed的channel中接收值:

ch:=make(chanint)\nclose(ch)\nx:=<-ch

如果channel中有值,这里特指带buffer的channel,那么就从channel中取,如果没有值,那么会返回channel元素的0值。

区分是返回的零值还是buffer中的值可使用comma,ok语法:

x,ok:=<-ch

若ok为false,表明channel已被关闭,所得的是无效的值。

nilchannel

不进行初始化,即不调用make来赋值的channel称为nilchannel:

varachanint

关闭一个nilchannel会直接panic

varachanint\nclose(a)//panic:closeofnilchannel

closeprinciple

一个sender,多个receiver,由sender来关闭channel,通知数据已发送完毕。

一旦sender有多个,可能就无法判断数据是否完毕了。这时候可以借助外部额外channel来做信号广播。这种做法类似于donechannel,或者stopchannel。

如果确定不会有goroutine在通信过程中被阻塞,也可以不关闭channel,等待GC对其进行回收。

源码分析

hchan

hchan是channel在runtime中的数据结构

//channel在runtime中的结构体\ntypehchanstruct{\n//队列中目前的元素计数\nqcountuint//totaldatainthequeue\n//环形队列的总大小,ch:=make(chanint,10)=>就是这里这个10\ndataqsizuint//sizeofthecircularqueue\n//void*的内存buffer区域\nbufunsafe.Pointer//pointstoanarrayofdataqsizelements\n//sizeofchan中的数据\nelemsizeuint16\n//是否已被关闭\ncloseduint32\n//runtime._type,代表channel中的元素类型的runtime结构体\nelemtype*_type//elementtype\n//发送索引\nsendxuint//sendindex\n//接收索引\nrecvxuint//receiveindex\n//接收goroutine对应的sudog队列\nrecvqwaitq//listofrecvwaiters\n//发送goroutine对应的sudog队列\nsendqwaitq//listofsendwaiters\n//lockprotectsallfieldsinhchan,aswellasseveral\n//fieldsinsudogsblockedonthischannel.\n//\n//DonotchangeanotherG&34;makechan:invalidchannelelementtype&34;makechan:badalignment&34;makechan:sizeoutofrange&39;sarereferencedfromtheirowningthreadsotheycan&34;chansend(nilchan)&34;unreachable&39;readyforsending&39;notreadyforsending&39;tclosedduringthefirstobservation.\nif!block&&c.closed==0&&((c.dataqsiz==0&&c.recvq.first==nil)||\n(c.dataqsiz>0&&c.qcount==c.dataqsiz)){\nreturnfalse\n}\nvart0int64\nifblockprofilerate>0{\nt0=cputicks()\n}\nlock(&c.lock)\n//channel已被关闭,panic\nifc.closed!=0{\nunlock(&c.lock)\npanic(plainError(&34;))\n}\nifsg:=c.recvq.dequeue();sg!=nil{\n//Foundawaitingreceiver.Wepassthevaluewewanttosend\n//directlytothereceiver,bypassingthechannelbuffer(ifany).\n//寻找一个等待中的receiver\n//越过channel的buffer\n//直接把要发的数据拷贝给这个receiver\n//然后就返\nsend(c,sg,ep,func(){unlock(&c.lock)},3)\nreturntrue\n}\n//qcount是buffer中已塞进的元素数量\n//dataqsize是buffer的总大小\n//说明还有余量\nifc.qcount<c.dataqsiz{\n//Spaceisavailableinthechannelbuffer.Enqueuetheelementtosend.\nqp:=chanbuf(c,c.sendx)\n//将goroutine的数据拷贝到buffer中\ntypedmemmove(c.elemtype,qp,ep)\n//将发送index加一\nc.sendx++\n//环形队列,所以如果已经加到最大了,就会0\nifc.sendx==c.dataqsiz{\nc.sendx=0\n}\n//将buffer的元素计数+1\nc.qcount++\nunlock(&c.lock)\nreturntrue\n}\nif!block{\nunlock(&c.lock)\nreturnfalse\n}\n//Blockonthechannel.Somereceiverwillcompleteouroperationforus.\n//在channel上阻塞,receiver会帮我们完成后续的工作\ngp:=getg()\nmysg:=acquireSudog()\nmysg.releasetime=0\nift0!=0{\nmysg.releasetime=-1\n}\n//Nostacksplitsbetweenassigningelemandenqueuingmysg\n//ongp.waitingwherecopystackcanfindit.\n//打包sudog\nmysg.elem=ep\nmysg.waitlink=nil\nmysg.g=gp\nmysg.isSelect=false\nmysg.c=c\ngp.waiting=mysg\ngp.param=nil\n//将当前这个发送goroutine打包后的sudog入队到channel的sendq队列中\nc.sendq.enqueue(mysg)\n//将这个发送g从Grunning->Gwaiting\n//进入休眠\ngoparkunlock(&c.lock,&34;,traceEvGoBlockSend,3)\n//someonewokeusup.\n//这里是被唤醒后要执行的代码\nifmysg!=gp.waiting{\n//先判断当前是不是合法的休眠中\nthrow(&34;)\n}\ngp.waiting=nil\nifgp.param==nil{\nifc.closed==0{\nthrow(&34;)\n}\n//唤醒后发现channel被人关了,气啊\npanic(plainError(&34;))\n}\ngp.param=nil\nifmysg.releasetime>0{\nblockevent(mysg.releasetime-t0,2)\n}\nmysg.c=nil\nreleaseSudog(mysg)\nreturntrue\n}\n//sendprocessesasendoperationonanemptychannelc.\n//Thevalueepsentbythesenderiscopiedtothereceiversg.\n//Thereceiveristhenwokenuptogoonitsmerryway.\n//Channelcmustbeemptyandlocked.sendunlockscwithunlockf.\n//sgmustalreadybedequeuedfromc.\n//epmustbenon-nilandpointtotheheaporthecaller&39;tneedtocheckep,asitisalwaysonthestack\n//orisnewmemoryallocatedbyreflect.\n//如果在nilchannel上进行recv操作,那么会永远阻塞\nifc==nil{\nif!block{\n//非阻塞的情况下\n//要直接返回\n//非阻塞出现在一些select的场景中\n//参见selectnbrecv/selectnbrecv2\nreturn\n}\n//当前goroutine:Grunning->Gwaiting\n//其实就是该goroutine直接泄露leak了\ngopark(nil,nil,&34;,traceEvGoStop,2)\n//放个throw有点莫名其妙\n//不过这段代码确实永远达到不了\nthrow(&34;)\n}\n//Fastpath:checkforfailednon-blockingoperationwithoutacquiringthelock.\n//\n//Afterobservingthatthechannelisnotreadyforreceiving,weobservethatthe\n//channelisnotclosed.Eachoftheseobservationsisasingleword-sizedread\n//(firstc.sendq.firstorc.qcount,andsecondc.closed).\n//Becauseachannelcannotbereopened,thelaterobservationofthechannel\n//beingnotclosedimpliesthatitwasalsonotclosedatthemomentofthe\n//firstobservation.Webehaveasifweobservedthechannelatthatmoment\n//andreportthatthereceivecannotproceed.\n//\n//Theorderofoperationsisimportanthere:reversingtheoperationscanleadto\n//incorrectbehaviorwhenracingwithaclose.\nif!block&&(c.dataqsiz==0&&c.sendq.first==nil||\nc.dataqsiz>0&&atomic.Loaduint(&c.qcount)==0)&&\natomic.Load(&c.closed)==0{\n//非阻塞且没内容可收的情况下要直接返回\n//两个bool的零值就是false,false\nreturn\n}\nvart0int64\nifblockprofilerate>0{\nt0=cputicks()\n}\nlock(&c.lock)\n//当前channel中没有数据可读\n//直接返回notselected\nifc.closed!=0&&c.qcount==0{\nunlock(&c.lock)\nifep!=nil{\ntypedmemclr(c.elemtype,ep)\n}\nreturntrue,false\n}\n//sender队列中有sudog在等待\n//直接从该sudog中获取数据拷贝到当前g即可\nifsg:=c.sendq.dequeue();sg!=nil{\n//Foundawaitingsender.Ifbufferissize0,receivevalue\n//directlyfromsender.Otherwise,receivefromheadofqueue\n//andaddsender&34;chanreceive&34;Gwaitinglistiscorrupted&39;sdataisputinthe\n//channelbuffer.\n//Channelcmustbefullandlocked.recvunlockscwithunlockf.\n//sgmustalreadybedequeuedfromc.\n//Anon-nilepmustpointtotheheaporthecaller&34;closeofnilchannel&34;closeofclosedchannel&39;vedroppedthechannellock.\nforglist!=nil{\ngp:=glist\nglist=glist.schedlink.ptr()\ngp.schedlink=0\n//诗g的状态切换到Grunnable\ngoready(gp,3)\n}\n}

回收类网站源码分享的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于回收网址、回收类网站源码分享的信息别忘了在本站进行查找哦。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平