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

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

特斯拉加码上游合作!巴西淡水河谷确认与其签署长期供镍协议周五,巴西矿商淡水河谷公司(ValeS。A。)确认,它已经与特斯拉公司签署了一项长期协议,为特斯拉提供来自淡水河谷加拿大矿山的镍,这些矿山去年生产了7。6万吨镍。淡水河谷没有提供该被小米的真诚打动了?印度解除小米7。25亿美元资产冻结五一劳动节,当国内很多人开始休息的时候,印度可没休息,对小米下手了,扣押了小米集团存在印度的555亿卢比存款,约合48亿人民币。印度给出的理由是,小米集团以假冒成支付版权费的方式,激光雷达再惹舌战,埃安小鹏理想各执一词,谁才是最优解?近日,百度旗下智能汽车公司品牌集度曝光了自己的最新汽车机器人概念车,它首创了车前盖向双激光雷达布置方案,达成了不错的效果。没想到引来了业内著名大嘴巴李想的吐槽,他表示在机盖上或者保大事件,网络显示ip地址,身处国内心忧国外的大主播还吃香吗显示ip地址,并不说明什么。刚看了一个托马斯妈妈在荷兰的视频,她说身在荷兰,但显示的ip地址是北京。因为她用的是北京电信的号码,所以显示在北京。还有,即使青蛙1450,或第五纵队或霸榜!联发科的天玑芯片在安兔兔手机的性能排行榜上占据前五最近,安兔兔在其官方微博上公布了四月份的Android旗舰性能和次旗舰性能排名。在旗舰榜上,天玑9000终端vivoX80以102万的成绩位列榜单第四。在次旗舰排行榜上,天玑810中兴Axon40系列外观设计公布中置打孔屏一亿像素日前,中兴已经正式确认中兴Axon40系列将于5月9日新品发布会正式发售,随着日期的临近,中兴Axon40系列的细节也是更加清晰。今天网上曝光了一组中兴Axon40系列的外观设计,iPhone14系列外观确认?这也太丑了吧随着6月份苹果大会的临近,网上关于ios16的曝光也越来越多。同步9月份的新品iPhone也出现了各种爆料。整理了下近期的各种爆料,比较靠谱的是14pro系列大概率要用挖孔屏了捂脸AKM在2022年慕尼黑HIGHEND展会上推出新的旗舰DACAK4499EX将声音体验带到了一个全新的水平!AK4499EXAsahiKaseiMicrodevices(AKM)将在慕尼黑举行的IPS2022年高端展会上首次展示其新的旗舰立体声数模转换器(装机五大件,最易被忽视的硬盘,你选对了吗电脑作为我们日常处理事物和休闲娱乐的重要载体,大家都很熟悉了。然而种种原因引起的电脑卡顿反应慢,让很多用户很头疼究竟是升级主机硬件还是配置新机,一时间难以抉择。就拿小编自用的电脑为520表白哪款蓝牙耳机比较实用?蓝牙耳机舒适度推荐耳机从一出现到现在,一直都在更新换代,从旧款的耳机繁杂的耳机线到新款的蓝牙耳机一秒连接,到最后适用于生活中的多种场景,一直都在进步在更新,但旧款耳机和蓝牙耳机相比,无论是技术,音质拉勾CEO称大厂员工高薪是被惯坏了李宁要卖咖啡小米7。25亿美元资产解除冻结整理丨张俊雯来源丨投中网全球市场1美股三大指数集体收跌,均创逾10年以来最长周线下跌纪录6日收盘,美股三大指数集体收跌,纳指跌1。4,标普500指数跌0。56,道指跌0。3,均创逾
黑莓为什么还不放弃全键盘?确实弊大于利,但还是有需求起初,我在使用手机打字的时候并不在意有没有震动。在某次机缘巧合下,我打开了打字的震动开关,从此以后,我便离不开打字震动了。我个人非常享受按下每个按键所给予的反馈,再加上我是个静音党电视机顶盒和网络机顶盒哪个好?看到这个问题的时候倍感亲切,因为小编家里就是因为一个不能完全满足,所以选择两种都装了的人,为什么这么浪费呢?理由如下首先,小编家里是好几年前才买的智能电视,刚开始那会电视机机顶盒就华为路由H6测评鸿蒙OS加持,网络全屋WiFi无死角覆盖前言现代化社会生活的我们,电子化网络成为了基本需求,随着人们对互联网的依赖越来越重,各式各样,并且大量的智能产品走进千家万户。当然,这一切都需要基于互联网,而且对WiFi网络覆盖以宇宙中有哪些恐怖的天体?比黑洞更可怕的有吗?宇宙中可怕天体很多,除了孕育生命的地球之外,对人类来讲,没有哪些天体是不可怕的。从寒冷寂寞的月球,到酷热熔炉一般的金星,再到不断释放光和热的太阳,再到中子星,超新星,黑洞,这些引力大咖谈城市大脑中国最有机会在全球率先完成城市数字化5月20日开幕的第五届世界智能大会上,中国工程院院士高文,中国工程院院士阿里云创始人王坚,科大讯飞董事长刘庆峰分别围绕城市大脑和智慧城市分享了自己的观点。高文认为,最理想的城市大脑时间真的是错觉吗?牛顿爱因斯坦早已解释过,世人不愿接受真相自2021年来,科学家精确地测量出这一年我们的时间正在加快。这是因为地球的自转速度正在以人类不可知的速度发生改变,随之带来的影响就是原来一天的时间是24小时,现在变成了23小时59中国电信强行要来的客户满意有何意义最近收到了很多中国电信多个省市一线装维员工的吐槽爆料,重点关注了中国电信一线装维员工的状况,一些爆料信息已经发布,也引起了一些关注和共鸣,还有一些爆料信息正在整理中,后面会陆续发布海南电信首推五星宽带标准光纤到达每个房间新海南客户端南海网南国都市报4月15日消息(记者任桐)4月15日,中国电信海南公司(以下简称海南电信)以用户需求和业务感知为根本导向,面向全省率先推出五星宽带。海南电信副总经理陈建天上不会掉馅饼,中国电信免费送手机的套路上个月月底,在外工作的小叔子打电话回来告知我,中国电信工作人员打电话给他说能免费送手机或者电视机,小叔子想领一个手机给家里妈妈用,他说需要让本人或家里人到指定地方领取。因为工作原因中国电信云南公司天翼云为您守护网络信息安全近年来,中国电信在云南充分发挥信息化主力军优势,持续提升基础网络品质与服务质量,在云网融合的时代背景下,围绕网是基础云为核心网随云动云网一体的发展原则,强化基础网络建设,推进云技术工信部研制APP收集使用个人信息等重要领域数据安全标准个人信息保护和数据安全监管或将强化。4月14日,国新办举行新闻发布会介绍打击治理电信网络诈骗犯罪工作进展情况。针对如何监管过度收集个人信息的问题,工业和信息化部网络安全管理局局长隋