设为首页 加入收藏

TOP

go channel原理及使用场景(四)
2023-07-23 13:29:02 】 浏览:90
Tags:channel
会做一些释放资源的操作

if sg := c.sendq.dequeue(); sg != nil {
		recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
		return true, true
	}


func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {
	if c.dataqsiz == 0 {
		if raceenabled {
			racesync(c, sg)
		}
		if ep != nil {
			// 如果无缓存,直接从发送者拷贝数据
			recvDirect(c.elemtype, sg, ep)
		}
	} else {
		// 由于队列已满,接收数据的索引和发送数据的索引一致
    qp := chanbuf(c, c.recvx)
		if raceenabled {
			racenotify(c, c.recvx, nil)
			racenotify(c, c.recvx, sg)
		}
		// 数据从队列拷贝到目标内存地址
		if ep != nil {
			typedmemmove(c.elemtype, ep, qp)
		}
		// 数据从发送者拷贝到缓冲区
		typedmemmove(c.elemtype, qp, sg.elem)
		c.recvx++
		if c.recvx == c.dataqsiz {
			c.recvx = 0
		}
		c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz
	}
	sg.elem = nil
	gp := sg.g
	unlockf()
	gp.param = unsafe.Pointer(sg)
	sg.success = true
	if sg.releasetime != 0 {
		sg.releasetime = cputicks()
	}
  // 无论发生哪种情况,运行时都会调用 runtime.goready 将当前处理器的 runnext 设置成发送数据的 Goroutine,在调度器下一次调度时将阻塞的发送方唤醒。
	goready(gp, skip+1)
}
  • 当缓冲区存在数据时,从 Channel 的缓冲区中接收数据

    if c.qcount > 0 {
    		// 直接从队列取数据
    		qp := chanbuf(c, c.recvx)
    		if raceenabled {
    			racenotify(c, c.recvx, nil)
    		}
      	// 放到目标内存
    		if ep != nil {
    			typedmemmove(c.elemtype, ep, qp)
    		}
      	// 清空队列中对应的元素
    		typedmemclr(c.elemtype, qp)
      	// 接收索引+1
    		c.recvx++
    		if c.recvx == c.dataqsiz {
    			c.recvx = 0
    		}
      	// 队列元素-1
    		c.qcount--
    		unlock(&c.lock)
    		return true, true
    	}
    
  • 当缓冲区中不存在数据时,等待其他 Goroutine 向 Channel 发送数据

    if !block {
    		unlock(&c.lock)
    		return false, false
    	}
    
    	// no sender available: block on this channel.
    	gp := getg()
    	mysg := acquireSudog()
    	mysg.releasetime = 0
    	if t0 != 0 {
    		mysg.releasetime = -1
    	}
    	// No stack splits between assigning elem and enqueuing mysg
    	// on gp.waiting where copystack can find it.
    	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)
    
    	// 唤醒之后清空sudog
    	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)
    	}
    	success := mysg.success
    	gp.param = nil
    	mysg.c = nil
    	releaseSudog(mysg)
    	return true, success
    
  • 关闭channel

    • 当 Channel 是一个空指针或者已经被关闭时,Go 语言运行时都会直接崩溃并抛出异常

      func closechan(c *hchan) {
      	if c == nil {
      		panic(plainError("close of nil channel"))
      	}
      
      	lock(&c.lock)
      	if c.closed != 0 {
      		unlock(&c.lock)
      		panic(plainError("close of closed channel"))
      	}
      
    • recvqsendq 两个队列中的数据加入到 Goroutine 列表 gList 中,与此同时该函数会清除所有 runtime.sudog 上未被处理的元素

      c.closed = 1
      
      	var glist gList
      
      	// release all readers
      	for {
      		sg := c.recvq.dequeue()
      		if sg == nil {
      			break
      		}
      		if sg.elem != nil {
      			typedmemclr(c.elemtype, sg.elem)
      			sg.elem = nil
      		}
      		if sg.releasetime != 0 {
      			sg.releasetime = cputicks()
      		}
      		gp := sg.g
      		gp.param = unsafe.Pointer(sg)
      		sg.success = false
      		if raceenabled {
      			raceacquireg(gp, c.raceaddr())
      		}
      		glist.push(gp)
      	}
      
      	// release all writers (they will panic)
      	for {
      		sg := c.sendq.dequeue()
      		if sg == nil {
      			break
      		}
      		sg.elem = nil
      		if sg.releasetime != 0 {
      			sg.r
    首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
    】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
    上一篇分享Go书籍-《Go Web编程》 下一篇Go设计模式学习准备——下载bilib..

    最新文章

    热门文章

    Hot 文章

    Python

    C 语言

    C++基础

    大数据基础

    linux编程基础

    C/C++面试题目