范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

channel原来就是个环形队列

  golang有一个很重要的特性就是channel,经常配合goroutine一起使用。一、基本用法初始化ch := make(chan bool)发送数据ch <- x接受数据x := <- ch x,ok := <- ch
  当然,其中也涉及到有缓冲和无缓冲的情况,为什么会造成这种情况,我们会在下面解释。二、数据结构type hchan struct { 	  qcount   uint           // 队列中数据的个数 	  dataqsiz uint           // 队列容量 	  buf      unsafe.Pointer // 存放在环形数组的数据 	  elemsize uint16         // channel中数据类型大小 	  closed   uint32         // channel是否关闭 	  elemtype *_type         // 元素类型 	  sendx    uint           // send的数组索引 	  recvx    uint           // receive的数组索引 	  recvq    waitq          // <-ch 阻塞在chan上的队列 list of recv waiters 	  sendq    waitq          // ch<- 阻塞在chan上的队列 list of send waiters 	  lock mutex }
  channel的数据结构不太复杂,就是一个环形队列,里面保存了长度qcount,容量dataqsiz,数据buf,以及前后索引sendx,recvx。
  closed用来标识channel的状态,0表示未关闭,非0表示已关闭,如果关闭,那么就不能发送数据。三、初始化
  在内部有两个make函数,一个是makechan64,一个是makechan,其实makechan64本质上还是调用的makechan。1、长度判断elem := t.elem 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")) }
  初始化的时候可以传入长度size,然后根据你初始化数据的类型大小elem.size计算是否有可用空间。2、分配内存var c *hchan switch { case mem == 0: 	  c = (*hchan)(mallocgc(hchanSize, nil, true)) 	  c.buf = c.raceaddr() case elem.ptrdata == 0: 	  c = (*hchan)(mallocgc(hchanSize+mem, nil, true)) 	  c.buf = add(unsafe.Pointer(c), hchanSize) default: 	  c = new(hchan) 	  c.buf = mallocgc(mem, elem, true) }如果size为0,只分配hchanSize的大小,如果是64位就是80,如果是32位就是40如果数据类型不是指针,分配一块连续内存hchanSize+mem如果数据类型是指针,hchan和buf单独分配
  此时,将结构体剩余字段赋值。c.elemsize = uint16(elem.size) c.elemtype = elem c.dataqsiz = uint(size)四、send
  就是ch <- x
  调用的函数签名是chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool1、空chan判断
  首先判断channel是否初始化if c == nil { 	  if !block { 	  	  return false 	  } 	  gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) 	  throw("unreachable") }
  其次判断channel是否关闭if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) || 	(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) { 	  return false }
  这段判断逻辑还是比较复杂的。
  这个是fast path,向没有阻塞的管道判断发送失败,这样就可以不用获取锁进行判断了。
  如果是非阻塞,管道没有关闭的情况下,没有缓冲区或缓冲区已经满了,返回false。
  由于这里是并发执行的,可能会在判断完c.closed==0之后,关闭channel,那么这里会出现这两种情况:
  1、channel没有关闭,没有缓冲区或缓冲区已经满了,返回false
  2、channel已经关闭,close会加锁将recvq和sendq全部出队列,返回false
  所以这里的判断是十分严谨的。2、加锁lock(&c.lock)  if c.closed != 0 { 	  unlock(&c.lock) 	  panic(plainError("send on closed channel")) }3、取出接受者if sg := c.recvq.dequeue(); sg != nil { 	  send(c, sg, ep, func() { unlock(&c.lock) }, 3) 	  return true }
  找到一个等待的接受者,直接发送4、是否有缓冲
  如果没有找到有等待的接受者,那么就看channel是否是有缓冲的。if c.qcount < c.dataqsiz { 	  qp := chanbuf(c, c.sendx) 	  typedmemmove(c.elemtype, qp, ep) 	  c.sendx++ 	  if c.sendx == c.dataqsiz { 	  	c.sendx = 0 	  } 	  c.qcount++ 	  unlock(&c.lock) 	  return true }
  可以看到环形队列的判断
  如果到这里,前面的步骤都没有发送成功,表示没有接受者等待,也没有缓冲区,那么就需要挂起goroutine等待接受者了。if !block { 	  unlock(&c.lock) 	  return false }5、阻塞goroutinegp := getg() mysg := acquireSudog() mysg.releasetime = 0 if t0 != 0 { 	  mysg.releasetime = -1 }  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) atomic.Store8(&gp.parkingOnChan, 1) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2)  KeepAlive(ep)
  添加发送者到发送队列,调用gopark阻塞6、发送完成if mysg != gp.waiting { 	  throw("G waiting list is corrupted") } gp.waiting = nil gp.activeStackChans = false 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)
  goroutine被唤醒后,表示发送完成,清理现场五、recvfunc chanrecv1(c *hchan, elem unsafe.Pointer) { 	  chanrecv(c, elem, true) }
  对应的是x <- chfunc chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {   	_, received = chanrecv(c, elem, true)   	return }
  对应的是x,ok :=<- ch1、空channel判断if c == nil { 	  if !block { 	  	return 	  } 	  gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) 	  throw("unreachable") }
  同样这里也有个fast pathif !block && (c.dataqsiz == 0 && c.sendq.first == nil || 	  c.dataqsiz > 0 && atomic.Loaduint(&c.qcount) == 0) && 	  atomic.Load(&c.closed) == 0 { 	  return }
  这里把是否关闭放在了最后进行判断,与发送不一样,这是因为接受的时候会走default分支c := make(chan int, 1) c <- 1 go func() {     select {     case <- c:         println("receive from c")     default:         println("c is not ready")     } } close(c)
  这段代码应该不执行default分支。
  可能会出现如下情况:select发生在close之前,从c中取出1select发送在close之后,但在<-c 之前,取出1select发送在<-c之后,取出0,received=false,但不会执行default
  如果这里我们把c.closed的判断放在前面的话,会出现以下情况:通道未关闭,不存在可接受数据,没有发送者等待,返回(false, false)通道已关闭,不存在可接受数据,没有发送者等待,应该要返回(ture, false),这里返回了(false, false)
  这里,selected应该为true,所以把c.closed放在最后判断可以避免这种情况。2、通道关闭lock(&c.lock)  if c.closed != 0 && c.qcount == 0 { 	  unlock(&c.lock) 	  if ep != nil { 	  	  typedmemclr(c.elemtype, ep) 	  } 	  return true, false }
  如果通道已经关闭并且没有数据可以读取,返回(true,false)3、取出发送者if sg := c.sendq.dequeue(); sg != nil { 	  recv(c, sg, ep, func() { unlock(&c.lock) }, 3) 	  return true, true }
  找到一个发送者,接受数据4、是否有缓冲if c.qcount > 0 { 	  qp := chanbuf(c, c.recvx) 	  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 }5、阻塞goroutinegp := getg() mysg := acquireSudog() mysg.releasetime = 0 if t0 != 0 { 	  mysg.releasetime = -1 }  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) atomic.Store8(&gp.parkingOnChan, 1) gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2)6、接受完成if mysg != gp.waiting { 	  throw("G waiting list is corrupted") } gp.waiting = nil gp.activeStackChans = false if mysg.releasetime > 0 { 	  blockevent(mysg.releasetime-t0, 2) } closed := gp.param == nil gp.param = nil mysg.c = nil releaseSudog(mysg)六、关闭channel
  这部分比较简单,就是加锁,设置标识位c.closed,然后唤醒所有的接受者和发送者,接受和发送数据,最后释放锁。七、总结
  1、channel底层就是一个环形队列
  2、在有接受者或发送者的情况下,在select中不会走default分支
  3、初始化的缓冲就是对应的是否阻塞block

奥特曼收集,到目前出现过的奥特曼有哪些(2)银河奥特曼(UltramanGinga,)身高微型40米无限大体重040000吨无限大人间体礼堂光变身器银河火花绝招银河穿击光线礼堂光用神秘道具银河火花变身成的神秘英雄。一般情况下欧洲杯历史首个两届赞助席位被vivo拿下,这次苹果三星钱给少了说起手机界的跨界,有人认为华为玩的十分通透,毕竟保时捷系列收获了一大批粉丝的心,而这次vivo用实际表现证明了自己同样也能把跨界玩出花样来!在今年6月,欧洲杯再次闯入各大球迷的眼界前端CSS学习笔记泡泡效果效果视频加载中代码!DOCTYPEhtmlhtmllangenheadmetacharsetutf8styletypetextcss。boxwidth343pxheight100p前端CSS学习笔记2D破碎盾牌复原过渡效果效果视频加载中代码!DOCTYPEhtmlhtmlheadlangenmetacharsetUTF8titletitlestyletypetextcssbodybackground子弹短信火不过三个月这两天朋友圈到处都在刷屏子弹短信的二维码,这个像极了网易公开课的那些刷屏海报,能在朋友圈刷屏的除了海报h5小游戏等,还有拼多多,这次又增加了一个app子弹短信,大家看着兴趣很高涨,李佳琦薇娅高居淘系双雄平台流量形态成金字塔?10月20日,淘宝双十一首轮预售鸣锣开卖,当日除了天量的成交规模以外,最引人注意的恐怕就是淘系两大主播李佳琦薇娅的个人战绩!当天薇娅和李佳琦的淘宝直播间一晚上的观看人数都超过了2个玖月奇迹为什么比不过凤凰传奇?在2011年,第一次登上春晚舞台的玖月奇迹一炮而红,在大学期间,两人因喜欢音乐而结缘,并组建了玖月奇迹这个组合参加星光大道,从星光大道出名后,这两人的事业也是一路顺风顺水,并且关系人猿星球将成现实?英伟达集齐三芯召唤神龙?图灵周报图灵周报精选AI行业一周大事件,从良莠不齐的行业资讯中挑选出最有价值的信息,配上专业点评,值得你细读品味。01微软160亿美元收购AI语音公司Nuance当地时间4月12日,微软和不只有三变!TFC威震天多形态试模展示去年9月份跟兄弟们分享过一款TFC三变威震天的资讯,个人对这款产品还是非常感兴趣的,毕竟可以跟他们家滚雷擎天柱搭配。时隔近一年,这款产品终于有新动态了,今天就跟兄弟们简单的分享一下共享单车涨价?资本肆虐后的穷途末路这几天共享单车涨价成了热点,继小蓝车涨价之后,从今天(日)起,摩拜单车也在北京地区实行了新的计费规则。摩拜单车起步价调整为元分钟骑行超出分钟,每分钟收费元。看来在资本肆虐过后,共享共享单车涨价?资本肆虐后的穷途末路这几天共享单车涨价成了热点,继小蓝车涨价之后,从今天(日)起,摩拜单车也在北京地区实行了新的计费规则。摩拜单车起步价调整为元分钟骑行超出分钟,每分钟收费元。看来在资本肆虐过后,共享
小米墨水屏高精度干湿电子表78元四季必备要知道,温湿度时刻都会影响我们的呼吸道健康和皮肤健康。尤其是在冬季,更不能忽视湿度和室温的变化。今天为大家推荐的小米电子表温湿度监测计Pro,内置的高精度传感器可准确感知温湿度的细鞭牛晚报小米12号创始员工离职三只松鼠总监受贿530万被判刑编者按鞭牛士将以晚报形式盘点今日内发生的重要事件,内容涵盖国际国内科技互联网,为科技行业从业者用户传递行业信息。国内新闻1小米12号创始员工离职,雷军再失猛将据新浪科技消息,小米创出门不用带手机!盘点5款智能手表,好用性价比高,苹果华为上榜如今随着科技的发达,智能产品在我们生活中的地位越来越高,智能手机也越来越普及。由于智能手机能打电话能通微信,一定程度上代替了手机的通讯功能,而且还能进行健康检测,因此也成为很多想逃华为官网上架麒麟9000新机,12GB1TB存储,售价高达20999元虽然花粉最期待的华为Mate50和华为P60系列迟迟未发布,但是华为官网最近偷偷上架了一款新机,依然搭载的是麒麟9000芯片,感兴趣的朋友一起来看看吧。华为官网最近上架了一款华为M2022年买手机必须买5g手机吗?手机店老板分析,不再纠结4g还是5g2022年买手机必须买5g手机吗?手机店老板分析,不再纠结4q还是5ghello大家好我是肖肖关注肖肖了解更多手机知识随着5g网络的不断宣传与普及,各大手机品牌的5g全面上市,大多不用进口光刻机?国产芯找到新材料,功耗降低50倍如今,在美国修改规则以及全球缺芯的双重冲击之下,我国在芯片方面和世界相比还是有点差距。因此,近年来,我国企业屡屡被外国卡脖子,其中最典型的代表就是华为。而之所以会被卡脖子,最重要的头条新闻之热点大事件头条新闻之热点大事件小鹏汽车回应高管年薪4。35亿元为错误解读16日,针对网传小鹏汽车某高管个人年薪为4。35亿元的消息,小鹏汽车官方回应称,该高管所持股份的价值被错误解读为年薪。新突破!WiFi7即将登场2月15日,高通官方发文介绍了WiFi7技术,并表示WiFi7即将开启连接领域的新篇章。在高通官网上赫然显示我们创新的WiFi7解决方案为下一代WiFi设定了标准。赋予了WiFi7安顾中国首席商务官郑路遥数字化进程更像是一场马拉松随着新一代信息技术加速发展,科技赋能带动保险行业持续转型升级,互联网已成为加快行业高质量发展最具活力的因素。中国目前已成长为全球第二大保险市场,未来以高科技为载体的保险服务化将是我推荐11款精致台式洗碗机,免安装超省事台式洗碗机的出现,解决了不少人的烦恼,不用在出租屋或老式房屋内打墙嵌入,也不用占据水槽空间,不需安装,直接放置使用更省事。接下来给大家推荐11款精致台式洗碗机,自带烘干功能,碗盘更如何理解丑人多上镜,美人不上镜这句话?不知道,反正我知道我丑,上不上镜都还是丑一般小脸上镜,这也就是为什么现在都整成锥子脸的缘故,而锥子脸真人是不好看的,而真正好看的鹅蛋脸标准东方美人脸,如果角度不对确实上镜不占优势,