当前位置: 首页 > news >正文

golang channel源码

解析

数据结构

hchan:channel 数据结构
qcount:当前 channel 中存在多少个元素;
dataqsize: 当前 channel 能存放的元素容量;
buf:channel 中用于存放元素的环形缓冲区;
elemsize:channel 元素类型的大小;
closed:标识 channel 是否关闭;
elemtype:channel 元素类型;
sendx:发送元素进入环形缓冲区的 index;
recvx:接收元素所处的环形缓冲区的 index;
recvq:因接收而陷入阻塞的协程队列;
sendq:因发送而陷入阻塞的协程队列;

type hchan struct {qcount   uint           // total data in the queuedataqsiz uint           // size of the circular queuebuf      unsafe.Pointer // points to an array of dataqsiz elementselemsize uint16closed   uint32timer    *timer // timer feeding this chanelemtype *_type // element typesendx    uint   // send indexrecvx    uint   // receive indexrecvq    waitq  // list of recv waiterssendq    waitq  // list of send waiters// lock protects all fields in hchan, as well as several// fields in sudogs blocked on this channel.//// Do not change another G's status while holding this lock// (in particular, do not ready a G), as this can deadlock// with stack shrinking.lock mutex
}

waitq:阻塞的协程队列
first:队列头部
last:队列尾部

type waitq struct {first *sudoglast  *sudog
}

//转到:链接名reflect_makechan reflect.makechan
func reflect_makechan(t *chantype, size int) *hchan {return makechan(t, size)
}func makechan64(t *chantype, size int64) *hchan {if int64(int(size)) != size {panic(plainError("makechan: size out of range"))}return makechan(t, int(size))
}func makechan(t *chantype, size int) *hchan {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"))}//当buf中存储的元素不包含指针时,Hchan不包含GC感兴趣的指针。//buf指向相同的分配,elemtype是持久的。//SudoG是从其所属线程引用的,因此无法收集它们。//TODO(dvyukov,rlh):重新思考收集器何时可以移动分配的对象。var c *hchanswitch {case mem == 0://队列或元素大小为零。c = (*hchan)(mallocgc(hchanSize, nil, true))
//竞赛检测器使用此位置进行同步。c.buf = c.raceaddr()case !elem.Pointers()://元素不包含指针。//在一次通话中分配hchan和buf。c = (*hchan)(mallocgc(hchanSize+mem, nil, true))c.buf = add(unsafe.Pointer(c), hchanSize)default://元素包含指针。c = new(hchan)c.buf = mallocgc(mem, elem, true)}c.elemsize = uint16(elem.Size_)c.elemtype = elemc.dataqsiz = uint(size)lockInit(&c.lock, lockRankHchan)if debugChan {print("makechan: chan=", c, "; elemsize=", elem.Size_, "; dataqsiz=", size, "\n")}return c
}//chanbuf(c,i)是指向缓冲区中第i个槽的指针。
//
//chanbuf应该是一个内部细节,
//但广泛使用的包使用linkname访问它。
//耻辱大厅的著名成员包括:
//-github/fjl/memsize
//
//不要删除或更改类型签名。
//见go.dev/issue/67401。
//
//go:链接名chanbuf
func chanbuf(c *hchan, i uint) unsafe.Pointer {return add(c.buf, uintptr(i)*uintptr(c.elemsize))
}//full报告c上的发送是否会阻塞(即通道已满)。
//它使用可变状态的单个字大小的读取,因此尽管
//答案瞬间为真,正确答案可能已经改变
//当调用函数接收到返回值时。
func full(c *hchan) bool {//c.dataqsiz是不可变的(在创建通道后从不写入)
//因此,在信道操作期间的任何时候都可以安全地读取。if c.dataqsiz == 0 {
//假设指针读取是原子性的。return c.recvq.first == nil}
//假设uint读取是轻松原子的。return c.qcount == c.dataqsiz
}//编译代码中c<-x的入口点。
//
//去:nosplit
func chansend1(c *hchan, elem unsafe.Pointer) {chansend(c, elem, true, getcallerpc())
}/*
*通用单通道发送/接收
*如果block不是nil,
*那么协议将不会
*睡觉,但如果可以的话就回来
*不完整。
*
*睡眠可以用g.param==nil唤醒
*当睡眠中涉及的通道
*已关闭。最容易循环并重新运行
*手术;我们会看到它现在已经关闭了。
*/
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {if c == nil {if !block {return false}gopark(nil, nil, waitReasonChanSendNilChan, traceBlockForever, 2)throw("unreachable")}if debugChan {print("chansend: chan=", c, "\n")}if raceenabled {racereadpc(c.raceaddr(), callerpc, abi.FuncPCABIInternal(chansend))}//快速路径:检查未获取锁的非阻塞操作是否失败。
//
//在观察到通道未关闭后,我们观察到通道
//未准备好发送。这些观察中的每一个都是一个单词大小的阅读
//(第一个c.closed和第二个full())。
//因为封闭通道无法从“准备发送”转换为
//“未准备好发送”,即使通道在两次观测之间关闭,
//它们暗示了两者之间的某个时刻,当时通道都还没有关闭
//并且尚未准备好发送。我们表现得好像在那一刻观察到了通道,
//并报告发送无法继续。
//
//如果在这里对读取进行重新排序是可以的:如果我们观察到通道不是
//准备发送,然后观察到它没有关闭,这意味着
//在第一次观察期间,通道没有关闭。然而,这里什么都没有
//保证向前发展。我们依靠解锁的副作用
//chanrecv()和closechan()来更新这个线程的c.closed和full()视图。if !block && c.closed == 0 && full(c) {return false}var t0 int64if blockprofilerate > 0 {t0 = cputicks()}lock(&c.lock)if c.closed != 0 {unlock(&c.lock)panic(plainError("send on closed channel"))}if sg := c.recvq.dequeue(); sg != nil {
//找到一个正在等待的接收器。我们传递要发送的值
//绕过信道缓冲器(如果有的话)直接传输到接收器。send(c, sg, ep, func() { unlock(&c.lock) }, 3)return true}if c.qcount < c.dataqsiz {
//通道缓冲区中有可用空间。取消要发送的元素的队列。qp := chanbuf(c, c.sendx)if raceenabled {racenotify(c, c.sendx, nil)}typedmemmove(c.elemtype, qp, ep)c.sendx++if c.sendx == c.dataqsiz {c.sendx = 0}c.qcount++unlock(&c.lock)return true}if !block {unlock(&c.lock)return false}//封锁频道。某个接收器将为我们完成操作。gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}
//在分配elem和查询mysg之间没有堆栈拆分
//在gp.waiting上,copystack可以在哪里找到它。mysg.elem = epmysg.waitlink = nilmysg.g = gpmysg.isSelect = falsemysg.c = cgp.waiting = mysggp.param = nilc.sendq.enqueue(mysg)
//向任何试图缩小我们堆栈的人发出信号,表明我们正在努力
//把车停在航道上。此G状态之间的窗口
//当我们设置gp.activeTackChans时
//堆叠收缩。gp.parkingOnChan.Store(true)gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceBlockChanSend, 2)//确保发送的值保持有效,直到
//接收者将其复制出来。sudog有一个指针指向
//堆栈对象,但sudogs不被视为
//堆栈跟踪器。KeepAlive(ep)//有人把我们吵醒了。if mysg != gp.waiting {throw("G waiting list is corrupted")}gp.waiting = nilgp.activeStackChans = falseclosed := !mysg.successgp.param = nilif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}mysg.c = nilreleaseSudog(mysg)if closed {if c.closed == 0 {throw("chansend: spurious wakeup")}panic(plainError("send on closed channel"))}return true
}//send在空信道c上处理发送操作。
//发送方发送的值ep被复制到接收方sg。
//然后,接收器被唤醒,继续它的快乐之路。
//通道c必须为空并锁定。send使用unlockf解锁c。
//sg必须已经从c中退出。
//ep必须为非nil,并指向堆或调用者的堆栈。
func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) {if raceenabled {if c.dataqsiz == 0 {racesync(c, sg)} else {//假装我们穿过缓冲区,即使
//我们直接复制。请注意,我们需要递增
//仅在启用赛道时才显示头部/尾部位置。racenotify(c, c.recvx, nil)racenotify(c, c.recvx, sg)c.recvx++if c.recvx == c.dataqsiz {c.recvx = 0}c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz}}if sg.elem != nil {sendDirect(c.elemtype, sg, ep)sg.elem = nil}gp := sg.gunlockf()gp.param = unsafe.Pointer(sg)sg.success = trueif sg.releasetime != 0 {sg.releasetime = cputicks()}goready(gp, skip+1)
}// timerchandrain removes all elements in channel c's buffer.
// It reports whether any elements were removed.
// Because it is only intended for timers, it does not
// handle waiting senders at all (all timer channels
// use non-blocking sends to fill the buffer).
func timerchandrain(c *hchan) bool {// Note: Cannot use empty(c) because we are called// while holding c.timer.sendLock, and empty(c) will// call c.timer.maybeRunChan, which will deadlock.// We are emptying the channel, so we only care about// the count, not about potentially filling it up.if atomic.Loaduint(&c.qcount) == 0 {return false}lock(&c.lock)any := falsefor c.qcount > 0 {any = truetypedmemclr(c.elemtype, chanbuf(c, c.recvx))c.recvx++if c.recvx == c.dataqsiz {c.recvx = 0}c.qcount--}unlock(&c.lock)return any
}// Sends and receives on unbuffered or empty-buffered channels are the
// only operations where one running goroutine writes to the stack of
// another running goroutine. The GC assumes that stack writes only
// happen when the goroutine is running and are only done by that
// goroutine. Using a write barrier is sufficient to make up for
// violating that assumption, but the write barrier has to work.
// typedmemmove will call bulkBarrierPreWrite, but the target bytes
// are not in the heap, so that will not help. We arrange to call
// memmove and typeBitsBulkBarrier instead.func sendDirect(t *_type, sg *sudog, src unsafe.Pointer) {// src is on our stack, dst is a slot on another stack.// Once we read sg.elem out of sg, it will no longer// be updated if the destination's stack gets copied (shrunk).// So make sure that no preemption points can happen between read & use.dst := sg.elemtypeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.Size_)// No need for cgo write barrier checks because dst is always// Go memory.memmove(dst, src, t.Size_)
}func recvDirect(t *_type, sg *sudog, dst unsafe.Pointer) {// dst is on our stack or the heap, src is on another stack.// The channel is locked, so src will not move during this// operation.src := sg.elemtypeBitsBulkBarrier(t, uintptr(dst), uintptr(src), t.Size_)memmove(dst, src, t.Size_)
}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"))}if raceenabled {callerpc := getcallerpc()racewritepc(c.raceaddr(), callerpc, abi.FuncPCABIInternal(closechan))racerelease(c.raceaddr())}c.closed = 1var glist gList// release all readersfor {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.ggp.param = unsafe.Pointer(sg)sg.success = falseif 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 = nilif sg.releasetime != 0 {sg.releasetime = cputicks()}gp := sg.ggp.param = unsafe.Pointer(sg)sg.success = falseif raceenabled {raceacquireg(gp, c.raceaddr())}glist.push(gp)}unlock(&c.lock)// Ready all Gs now that we've dropped the channel lock.for !glist.empty() {gp := glist.pop()gp.schedlink = 0goready(gp, 3)}
}// empty reports whether a read from c would block (that is, the channel is
// empty).  It is atomically correct and sequentially consistent at the moment
// it returns, but since the channel is unlocked, the channel may become
// non-empty immediately afterward.
func empty(c *hchan) bool {// c.dataqsiz is immutable.if c.dataqsiz == 0 {return atomic.Loadp(unsafe.Pointer(&c.sendq.first)) == nil}// c.timer is also immutable (it is set after make(chan) but before any channel operations).// All timer channels have dataqsiz > 0.if c.timer != nil {c.timer.maybeRunChan()}return atomic.Loaduint(&c.qcount) == 0
}// entry points for <- c from compiled code.
//
//go:nosplit
func chanrecv1(c *hchan, elem unsafe.Pointer) {chanrecv(c, elem, true)
}//go:nosplit
func chanrecv2(c *hchan, elem unsafe.Pointer) (received bool) {_, received = chanrecv(c, elem, true)return
}// chanrecv receives on channel c and writes the received data to ep.
// ep may be nil, in which case received data is ignored.
// If block == false and no elements are available, returns (false, false).
// Otherwise, if c is closed, zeros *ep and returns (true, false).
// Otherwise, fills in *ep with an element and returns (true, true).
// A non-nil ep must point to the heap or the caller's stack.
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {// raceenabled: don't need to check ep, as it is always on the stack// or is new memory allocated by reflect.if debugChan {print("chanrecv: chan=", c, "\n")}if c == nil {if !block {return}gopark(nil, nil, waitReasonChanReceiveNilChan, traceBlockForever, 2)throw("unreachable")}if c.timer != nil {c.timer.maybeRunChan()}// Fast path: check for failed non-blocking operation without acquiring the lock.if !block && empty(c) {// After observing that the channel is not ready for receiving, we observe whether the// channel is closed.//// Reordering of these checks could lead to incorrect behavior when racing with a close.// For example, if the channel was open and not empty, was closed, and then drained,// reordered reads could incorrectly indicate "open and empty". To prevent reordering,// we use atomic loads for both checks, and rely on emptying and closing to happen in// separate critical sections under the same lock.  This assumption fails when closing// an unbuffered channel with a blocked send, but that is an error condition anyway.if atomic.Load(&c.closed) == 0 {// Because a channel cannot be reopened, the later observation of the channel// being not closed implies that it was also not closed at the moment of the// first observation. We behave as if we observed the channel at that moment// and report that the receive cannot proceed.return}// The channel is irreversibly closed. Re-check whether the channel has any pending data// to receive, which could have arrived between the empty and closed checks above.// Sequential consistency is also required here, when racing with such a send.if empty(c) {// The channel is irreversibly closed and empty.if raceenabled {raceacquire(c.raceaddr())}if ep != nil {typedmemclr(c.elemtype, ep)}return true, false}}var t0 int64if blockprofilerate > 0 {t0 = cputicks()}lock(&c.lock)if c.closed != 0 {if c.qcount == 0 {if raceenabled {raceacquire(c.raceaddr())}unlock(&c.lock)if ep != nil {typedmemclr(c.elemtype, ep)}return true, false}// The channel has been closed, but the channel's buffer have data.} else {// Just found waiting sender with not closed.if sg := c.sendq.dequeue(); sg != nil {// Found a waiting sender. If buffer is size 0, receive value// directly from sender. Otherwise, receive from head of queue// and add sender's value to the tail of the queue (both map to// the same buffer slot because the queue is full).recv(c, sg, ep, func() { unlock(&c.lock) }, 3)return true, true}}if c.qcount > 0 {// Receive directly from queueqp := chanbuf(c, c.recvx)if raceenabled {racenotify(c, c.recvx, nil)}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}if !block {unlock(&c.lock)return false, false}// no sender available: block on this channel.gp := getg()mysg := acquireSudog()mysg.releasetime = 0if t0 != 0 {mysg.releasetime = -1}// No stack splits between assigning elem and enqueuing mysg// on gp.waiting where copystack can find it.mysg.elem = epmysg.waitlink = nilgp.waiting = mysgmysg.g = gpmysg.isSelect = falsemysg.c = cgp.param = nilc.recvq.enqueue(mysg)if c.timer != nil {blockTimerChan(c)}// Signal to anyone trying to shrink our stack that we're about// to park on a channel. The window between when this G's status// changes and when we set gp.activeStackChans is not safe for// stack shrinking.gp.parkingOnChan.Store(true)gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceBlockChanRecv, 2)// someone woke us upif mysg != gp.waiting {throw("G waiting list is corrupted")}if c.timer != nil {unblockTimerChan(c)}gp.waiting = nilgp.activeStackChans = falseif mysg.releasetime > 0 {blockevent(mysg.releasetime-t0, 2)}success := mysg.successgp.param = nilmysg.c = nilreleaseSudog(mysg)return true, success
}// recv processes a receive operation on a full channel c.
// There are 2 parts:
//  1. The value sent by the sender sg is put into the channel
//     and the sender is woken up to go on its merry way.
//  2. The value received by the receiver (the current G) is
//     written to ep.
//
// For synchronous channels, both values are the same.
// For asynchronous channels, the receiver gets its data from
// the channel buffer and the sender's data is put in the
// channel buffer.
// Channel c must be full and locked. recv unlocks c with unlockf.
// sg must already be dequeued from c.
// A non-nil ep must point to the heap or the caller's stack.
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 {// copy data from senderrecvDirect(c.elemtype, sg, ep)}} else {// Queue is full. Take the item at the// head of the queue. Make the sender enqueue// its item at the tail of the queue. Since the// queue is full, those are both the same slot.qp := chanbuf(c, c.recvx)if raceenabled {racenotify(c, c.recvx, nil)racenotify(c, c.recvx, sg)}// copy data from queue to receiverif ep != nil {typedmemmove(c.elemtype, ep, qp)}// copy data from sender to queuetypedmemmove(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 = nilgp := sg.gunlockf()gp.param = unsafe.Pointer(sg)sg.success = trueif sg.releasetime != 0 {sg.releasetime = cputicks()}goready(gp, skip+1)
}func chanparkcommit(gp *g, chanLock unsafe.Pointer) bool {// There are unlocked sudogs that point into gp's stack. Stack// copying must lock the channels of those sudogs.// Set activeStackChans here instead of before we try parking// because we could self-deadlock in stack growth on the// channel lock.gp.activeStackChans = true// Mark that it's safe for stack shrinking to occur now,// because any thread acquiring this G's stack for shrinking// is guaranteed to observe activeStackChans after this store.gp.parkingOnChan.Store(false)// Make sure we unlock after setting activeStackChans and// unsetting parkingOnChan. The moment we unlock chanLock// we risk gp getting readied by a channel operation and// so gp could continue running before everything before// the unlock is visible (even to gp itself).unlock((*mutex)(chanLock))return true
}// compiler implements
//
//	select {
//	case c <- v:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if selectnbsend(c, v) {
//		... foo
//	} else {
//		... bar
//	}
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {return chansend(c, elem, false, getcallerpc())
}// compiler implements
//
//	select {
//	case v, ok = <-c:
//		... foo
//	default:
//		... bar
//	}
//
// as
//
//	if selected, ok = selectnbrecv(&v, c); selected {
//		... foo
//	} else {
//		... bar
//	}
func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {return chanrecv(c, elem, false)
}//go:linkname reflect_chansend reflect.chansend0
func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {return chansend(c, elem, !nb, getcallerpc())
}//go:linkname reflect_chanrecv reflect.chanrecv
func reflect_chanrecv(c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {return chanrecv(c, elem, !nb)
}func chanlen(c *hchan) int {if c == nil {return 0}async := debug.asynctimerchan.Load() != 0if c.timer != nil && async {c.timer.maybeRunChan()}if c.timer != nil && !async {// timer channels have a buffered implementation// but present to users as unbuffered, so that we can// undo sends without users noticing.return 0}return int(c.qcount)
}func chancap(c *hchan) int {if c == nil {return 0}if c.timer != nil {async := debug.asynctimerchan.Load() != 0if async {return int(c.dataqsiz)}// timer channels have a buffered implementation// but present to users as unbuffered, so that we can// undo sends without users noticing.return 0}return int(c.dataqsiz)
}//go:linkname reflect_chanlen reflect.chanlen
func reflect_chanlen(c *hchan) int {return chanlen(c)
}//go:linkname reflectlite_chanlen internal/reflectlite.chanlen
func reflectlite_chanlen(c *hchan) int {return chanlen(c)
}//go:linkname reflect_chancap reflect.chancap
func reflect_chancap(c *hchan) int {return chancap(c)
}//go:linkname reflect_chanclose reflect.chanclose
func reflect_chanclose(c *hchan) {closechan(c)
}func (q *waitq) enqueue(sgp *sudog) {sgp.next = nilx := q.lastif x == nil {sgp.prev = nilq.first = sgpq.last = sgpreturn}sgp.prev = xx.next = sgpq.last = sgp
}func (q *waitq) dequeue() *sudog {for {sgp := q.firstif sgp == nil {return nil}y := sgp.nextif y == nil {q.first = nilq.last = nil} else {y.prev = nilq.first = ysgp.next = nil // mark as removed (see dequeueSudoG)}// if a goroutine was put on this queue because of a// select, there is a small window between the goroutine// being woken up by a different case and it grabbing the// channel locks. Once it has the lock// it removes itself from the queue, so we won't see it after that.// We use a flag in the G struct to tell us when someone// else has won the race to signal this goroutine but the goroutine// hasn't removed itself from the queue yet.if sgp.isSelect && !sgp.g.selectDone.CompareAndSwap(0, 1) {continue}return sgp}
}func (c *hchan) raceaddr() unsafe.Pointer {// Treat read-like and write-like operations on the channel to// happen at this address. Avoid using the address of qcount// or dataqsiz, because the len() and cap() builtins read// those addresses, and we don't want them racing with// operations like close().return unsafe.Pointer(&c.buf)
}func racesync(c *hchan, sg *sudog) {racerelease(chanbuf(c, 0))raceacquireg(sg.g, chanbuf(c, 0))racereleaseg(sg.g, chanbuf(c, 0))raceacquire(chanbuf(c, 0))
}// Notify the race detector of a send or receive involving buffer entry idx
// and a channel c or its communicating partner sg.
// This function handles the special case of c.elemsize==0.
func racenotify(c *hchan, idx uint, sg *sudog) {// We could have passed the unsafe.Pointer corresponding to entry idx// instead of idx itself.  However, in a future version of this function,// we can use idx to better handle the case of elemsize==0.// A future improvement to the detector is to call TSan with c and idx:// this way, Go will continue to not allocating buffer entries for channels// of elemsize==0, yet the race detector can be made to handle multiple// sync objects underneath the hood (one sync object per idx)qp := chanbuf(c, idx)// When elemsize==0, we don't allocate a full buffer for the channel.// Instead of individual buffer entries, the race detector uses the// c.buf as the only buffer entry.  This simplification prevents us from// following the memory model's happens-before rules (rules that are// implemented in racereleaseacquire).  Instead, we accumulate happens-before// information in the synchronization object associated with c.buf.if c.elemsize == 0 {if sg == nil {raceacquire(qp)racerelease(qp)} else {raceacquireg(sg.g, qp)racereleaseg(sg.g, qp)}} else {if sg == nil {racereleaseacquire(qp)} else {racereleaseacquireg(sg.g, qp)}}
}

其他

const (maxAlign  = 8hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1))debugChan = false
)

相关文章:

golang channel源码

解析 数据结构 hchan&#xff1a;channel 数据结构 qcount&#xff1a;当前 channel 中存在多少个元素&#xff1b; dataqsize: 当前 channel 能存放的元素容量&#xff1b; buf&#xff1a;channel 中用于存放元素的环形缓冲区&#xff1b; elemsize&#xff1a;channel 元素…...

麒麟操作系统漏洞修复保姆级教程弱(一)算法漏洞修复

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 目录 一、相关问题 二、建议修复方法 修复方案&#xff08;方案一和方案二是错误示范&#xff0c;干货在方案三&#xff09; 方案一、首先我想按照第一步&#xff0c;将OpenSSH升级解决这一漏洞…...

汉诺塔专题:P1760 通天之汉诺塔 题解 + Problem D: 汉诺塔 题解

1. P1760 通天之汉诺塔 题解 题目背景 直达通天路小A历险记第四篇 题目描述 在你的帮助下&#xff0c;小 A 成功收集到了宝贵的数据&#xff0c;他终于来到了传说中连接通天路的通天山。但是这距离通天路仍然有一段距离&#xff0c;但是小 A 突然发现他没有地图&#xff0…...

vscode中markdown一些插件用不了解决方式

我发现我安装了vscode的一些插件&#xff0c;但是没起效果&#xff08;就是该插件暗淡了&#xff09;&#xff0c;后面得知&#xff0c;是因为没有信任工作空间。 This extension has been disabled because the current workspace is not trusted 这个提示信息表明&#xff0c…...

Python爬虫第16节-动态渲染页面抓取之Selenium使用上篇

目录 前言 一、Selenium的简介和学习准备 二、Selenium基本使用 三、声明浏览器对象 四、访问页面 五、查找节点 5.1 单个节点 5.2 多个节点 六、节点交互 七、动作链 八、执行JavaScript 前言 本专栏之前的内容&#xff0c;我们讲了怎么分析和抓取Ajax&#xff0c;…...

KMP算法动态演示

KMP算法 1.动态演示 https://tsccg-oss.oss-cn-guangzhou.aliyuncs.com/image/KMP%E7%AE%97%E6%B3%95%E5%8A%A8%E6%80%81%E6%BC%94%E7%A4%BA.gif 2.代码实现 Testpublic void test5(){String parent "ABC ABCDAB ABCDABCDABDE";String child "ABCDABD&quo…...

linux获取cpu使用率(sy%+us%)

float getCpuUsage() { // C11兼容的元组解包 typedef std::tuple<unsigned long long, unsigned long long, unsigned long long> CpuTuple; auto parseCpuLine [](const std::string& line) -> CpuTuple { std::istringstream iss(line); …...

ESP-IDF教程2 GPIO - 输入、输出和中断

文章目录 1、前提1.1、基础知识1.1.1、GPIO 分类1.1.2、FALSH SPI 模式1.1.3、过滤器1.1.4、外部中断 1.2、数据结构1.2.1、GPIO1.2.2、毛刺过滤器 1.3、硬件原理图 2、示例程序2.1、GPIO 输出 - 点亮 LED 灯2.2、GPIO 输入 - 按键响应2.3、GPIO 外部中断 - 按键响应 3、常用函…...

Ubuntu安装MySQL步骤及注意事项

一、安装前准备 1. 系统更新&#xff1a;在安装 MySQL 之前&#xff0c;确保你的 Ubuntu 系统软件包是最新的&#xff0c;这能避免因软件包版本问题导致的安装错误&#xff0c;并获取最新的安全补丁。打开终端&#xff0c;执行以下两条命令&#xff1a; sudo apt update sudo …...

支持mingw g++14.2 的c++23 功能print的vscode tasks.json生成调试

在mingw14.2版本中, print库的功能默认没有开启, 生成可执行文件的tasks.json里要显式加-lstdcexp, 注意放置顺序. tasks.json (支持mingw g14.2 c23的print ) {"version": "2.0.0","tasks": [{"type": "cppbuild","…...

WPF常用技巧汇总

主要用于记录工作中发现的一些问题和常见的解决方法。 此文会持续更新。 >abp new Evan.MyWpfApp -t wpf --old --framework .net8 1. 解决不同屏幕分辨率下的锯齿问题 UseLayoutRounding"True" <Grid UseLayoutRounding"True"><Border Mar…...

【文件操作与IO】详细解析文件操作与IO (一)

本篇博客给大家带来的是文件操作的知识点. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 一. …...

yaffs_write_new_chunk()函数解析

yaffs_write_new_chunk() 是 YAFFS&#xff08;Yet Another Flash File System&#xff09;文件系统中用于将数据写入新物理块&#xff08;chunk&#xff09;的关键函数。以下是其详细解析&#xff1a; 函数原型 int yaffs_write_new_chunk(struct yaffs_dev *dev, const u8 *…...

「数据可视化 D3系列」入门第九章:交互式操作详解

交互式操作详解 一、交互式操作的核心概念二、当前柱状图的交互实现三、交互效果的增强建议四、为饼图做准备五、交互式柱状图代码示例小结核心要点 下章预告&#xff1a;将这些交互技术应用于饼图实现 在上一章的柱状图基础上&#xff0c;我们增加了交互功能&#xff0c;让图表…...

吃透LangChain(五):多模态输入与自定义输出

多模态数据输入 这里我们演示如何将多模态输入直接传递给模型。我们目前期望所有输入都以与OpenAl 期望的格式相同的格式传递。对于支持多模态输入的其他模型提供者&#xff0c;我们在类中添加了逻辑以转换为预期格式。 在这个例子中&#xff0c;我们将要求模型描述一幅图像。 …...

数据结构基本概念

1 数据结构概述 数据结构是计算机组织&#xff08;逻辑结构&#xff09;、存储&#xff08;物理结构&#xff09;数据的方式。和具体的计算机编程语言无关&#xff0c;可以使用任何编程语言来实现数据结构 1.1 数据逻辑结构 反映数据元素之间的逻辑关系&#xff0c;逻辑关系…...

flutter app实现分辨率自适应的图片资源加载

在 Flutter 中&#xff0c;为了实现分辨率自适应的图片资源加载&#xff0c;确实需要遵循特定的目录结构和命名规则。这种机制允许 AssetImage 根据设备的 设备像素比&#xff08;Device Pixel Ratio, DPR&#xff09; 自动选择最合适的图片资源。以下是详细的说明和实现步骤&a…...

Webpack基础

目录 一、Webpack概念 二、Webpack使用步骤 三、Webpack.config.js配置文件 四、entry 和 output 1. entry 2. output 五、module 1. CSS 2. 图片 3. babel 4. 总结 六、plugins 1.自动生成 html 文件 七、其它 1.webpack-dev-server 开发服务器 2. mode 打包模…...

极狐GitLab 用户 API 速率限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 用户 API 速率限制 (BASIC SELF) 您可以为对 Users API 的请求配置每个用户的速率限制。 要更改速率限制&#xff1a; 1.在…...

用 Go 实现一个轻量级并发任务调度器(支持限速)

前言 在日常开发中&#xff0c;我们经常会遇到这样的场景&#xff1a; • 有一堆任务要跑&#xff08;比如&#xff1a;发请求、处理数据、爬虫等&#xff09;• 不希望一次性全部跑完&#xff0c;担心打爆服务端或者被封• 想要设置并发数、限速&#xff0c;还能控制任务重试…...

华为OD机试真题——最长的顺子(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 本文收录于专栏&#xff1a;《2025华为OD真题目录全流程解析/备考攻略/经验…...

3.8/Q1,GBD数据库最新文章解读

文章题目&#xff1a;Regional and National Burden of Traumatic Brain Injury and Spinal Cord Injury in North Africa and Middle East Regions, 1990-2021: A Systematic Analysis for The Global Burden of Disease Study 2021 DOI&#xff1a;10.1007/s44197-025-00372-…...

Java面试中问单例模式如何回答

1. 什么是单例模式&#xff1f; 单例模式&#xff08;Singleton Pattern&#xff09;是一种设计模式&#xff0c;确保某个类在整个应用中只有一个实例&#xff0c;并且提供全局访问点。它有以下特点&#xff1a; 确保只有一个实例。提供全局访问点。防止多次实例化&#xff0…...

再看开源多模态RAG的视觉文档(OCR-Free)检索增强生成方案-VDocRAG

前期几个工作提到&#xff0c;基于OCR的文档解析RAG的方式进行知识库问答&#xff0c;受限文档结构复杂多样&#xff0c;各个环节的解析泛化能力较差&#xff0c;无法完美的对文档进行解析。因此出现了一些基于多模态大模型的RAG方案。如下&#xff1a; 【RAG&多模态】多模…...

【go】什么是Go语言中的GC,作用是什么?调优,sync.Pool优化,逃逸分析演示

Go 语言中的 GC 简介与调优建议 一、GC 简介 Go 的 GC&#xff08;Garbage Collection&#xff09;用于自动管理内存&#xff0c;开发者无需手动释放内存&#xff0c;可以专注于业务逻辑&#xff0c;降低出错概率&#xff0c;提升开发效率。 GC 能够自动发现和回收不再使用的…...

ctfshow-大赛原题-web702

因为该题没有理解到位&#xff0c;导致看wp也一直出错&#xff0c;特此反思一下。 参考yu22x师傅的文章 &#xff1a;CTFSHOW大赛原题篇(web696-web710)_ctfshow 大赛原题-CSDN博客 首先拿到题目&#xff1a; // www.zip 下载源码 我们的思路就是包含一个css文件&#xff0c;…...

基于Redis的4种延时队列实现方式

延时队列是一种特殊的消息队列&#xff0c;它允许消息在指定的时间后被消费。在微服务架构、电商系统和任务调度场景中&#xff0c;延时队列扮演着关键角色。例如&#xff0c;订单超时自动取消、定时提醒、延时支付等都依赖延时队列实现。 Redis作为高性能的内存数据库&#x…...

基于Django实现农业生产可视化系统

基于Django实现农业生产可视化系统 项目截图 登录 注册 首页 农业数据-某一指标表格展示 农业数据-某一指标柱状图展示 农业数据-某一指标饼状图展示 气候数据-平均气温地图展示 气候数据-降水量合并图展示 后台管理 一、系统简介 农业生产可视化系统是一款基于DjangoMVTMyS…...

yocto编译使用共享缓存

注意 服务器端与客户端系统的版本号需为Ubuntu20.04执行用户需要为sudo权限服务器端nfs目录权限必须为nobody:nogroup 服务端配置&#xff1a; 在服务器192.168.60.142上配置 NFS 共享&#xff1a; 1.安装 NFS 服务器&#xff1a; 1 sudo apt-get install nfs-kernel-serve…...

uCOS3实时操作系统(系统架构和中断管理)

文章目录 系统架构中断管理ARM中断寄存器相关知识ucos中断机制 系统架构 ucos主要包含三个部分的源码&#xff1a; 1、OS核心源码及其配置文件&#xff08;ucos源码&#xff09; 2、LIB库文件源码及其配置文件&#xff08;库文件&#xff0c;比如字符处理、内存管理&#xff0…...

web后端语言下篇

#作者&#xff1a;允砸儿 #日期&#xff1a;乙巳青蛇年 三月廿一 笔者今天将web后端语言PHP完结一下&#xff0c;后面还会写一个关于python的番外。 PHP函数 PHP函数它和笔者前面写的js函数有些许类似&#xff0c;都是封装的概念。将实现某一功能的代码块封装到一个结构中…...

Tensorflow释放GPU资源

语言&#xff1a;python 框架&#xff1a;tensorflow 现有问题&#xff1a;用tensorflow进行模型训练&#xff0c;训练完成后用tf.keras.backend.clear_session()命令无法真正实现释放资源的效果。 解决方案&#xff1a;创建多进程&#xff0c;将模型训练作为子进程&#xff0c…...

vscode、cherry studio接入高德mcp服务

最近mcp协议比较火&#xff0c;好多平台都已经开通了mcp协议&#xff0c;今天来接入下高德的mcp看看效果如何。 话不多说&#xff0c;咱们直接开干。 先来看下支持mcp协议的工具有cusor、cline等等。更新cherrystudio后发现上面也有mcp服务器了。今天咱就来试试添加高德的mcp协…...

软考高级-系统架构设计师 论文范文参考(二)

文章目录 论企业应用集成论软件三层结构的设计论软件设计模式的应用论软件维护及软件可维护性论信息系统安全性设计论信息系统的安全性设计(二)论信息系统的架构设计论信息系统架构设计(二) 论企业应用集成 摘要: 2016年9月&#xff0c;我国某省移动通信有限公司决定启动VerisB…...

熵权法+TOPSIS+灰色关联度综合算法(Matlab实现)

熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09; 代码获取私信回复&#xff1a;熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09; 摘要&#xff1a; 熵权法TOPSIS灰色关联度综合算法&#xff08;Matlab实现&#xff09;代码实现了一种…...

【java 13天进阶Day05】数据结构,List,Set ,TreeSet集合,Collections工具类

常见的数据结构种类 集合是基于数据结构做出来的&#xff0c;不同的集合底层会采用不同的数据结构。不同的数据结构&#xff0c;功能和作用是不一样的。数据结构&#xff1a; 数据结构指的是数据以什么方式组织在一起。不同的数据结构&#xff0c;增删查的性能是不一样的。不同…...

极狐GitLab 议题和史诗创建的速率限制如何设置?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 议题和史诗创建的速率限制 (BASIC SELF) 速率限制是为了控制新史诗和议题的创建速度。例如&#xff0c;如果您将限制设置为 …...

AIGC-几款本地生活服务智能体完整指令直接用(DeepSeek,豆包,千问,Kimi,GPT)

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资…...

十、数据库day02--SQL语句01

文章目录 一、新建查询1.查询窗口的开启方法2. 单语句运行方法 二、数据库操作1.创建数据库2. 使用数据库3. 修改数据库4. 删除数据库和查看所有数据库5. 重点&#xff1a;数据库备份5.1 应用场景5.2 利用工具备份备份操作还原操作 5.3 扩展&#xff1a;使用命令备份 三、数据表…...

2025年MathorCup数学应用挑战赛D题问题一求解与整体思路分析

D题 短途运输货量预测及车辆调度 问题背景 问题分析&#xff1a;四个问题需要建立数学模型解决就业状态分析与预测&#xff0c;旨在通过数学建模对宜昌地区的就业数据进行深入分析&#xff0c;并基于此预测就业状态。提供的数据涵盖了被调查者的个人信息、就业信息、失业信息…...

关于三防漆清除剂

成分及原理 主要成分&#xff1a;通常包含有机溶剂&#xff0c;如丙酮、甲苯、二甲苯等&#xff0c;以及一些表面活性剂、缓蚀剂等添加剂。工作原理&#xff1a;有机溶剂能够溶解三防漆中的树脂等成分&#xff0c;使其失去粘性和附着性&#xff0c;从而可以被轻易地擦拭或冲洗…...

2025年MathorCup数学应用挑战赛【选题分析】

【25MathorCup选题分析】 &#x1f64b;‍♀&#x1f64b;‍♂数模加油站初步分析评估了此次竞赛题目&#xff1a; ✅A题&#xff1a;该题新颖性强&#xff0c;属于“算子学习深度学习几何建模”的交叉问题&#xff0c;涉及PINN、FNO、KAN等算子神经网络模型构建&#xff0c;任…...

在windows上交叉编译opencv供RK3588使用

环境 NDK r27、RK3588 安卓板子、Android 12 步骤操作要点1. NDK 下载选择 r27 版本&#xff0c;解压到无空格路径&#xff08;如 C:/ndk&#xff09;2. 环境变量配置添加 ANDROID_NDK_ROOT 和工具链路径到系统 PATH3. CMake 参数调整指定 ANDROID_NATIVE_API_LEVEL31、ANDRO…...

零基础玩转AI数学建模:从理论到实战

前言 数学建模作为连接数学理论与现实世界的桥梁&#xff0c;在科学研究、工程实践和商业决策等领域发挥着越来越重要的作用。随着人工智能技术的迅猛发展&#xff0c;以ChatGPT为代表的大语言模型为数学建模领域带来了革命性的变革。本书旨在帮助读者掌握这一变革带来的新机遇…...

IDEA 2025.1更新-AI助手试用和第三方模型集成方案

今天刚把 IntelliJ IDEA 更新到了 2025.1 版本&#xff0c;主要是想看看这次 AI Assistant 有什么新东西。之前看到消息说功能有更新&#xff0c;而且似乎可以免费试用&#xff0c;就动手试了试&#xff0c;顺便把过程和一些发现记录下来&#xff0c;给可能需要的朋友一个参考。…...

static关键字

思维导图&#xff1a; 在 Java 中&#xff0c;static 是一个非常重要的关键字&#xff0c;它可以用来修饰类的成员&#xff0c;包括变量、方法、代码块以及内部类。下面为你详细介绍 static 关键字的各种用法和特点。 一.修饰内部类 静态内部类&#xff1a;当 static 修饰内部类…...

gl-matrix 库简介

gl-matrix 库简介 gl-matrix 是一个高性能的 JavaScript 矩阵和向量库&#xff0c;专门为 WebGL 和其他 3D 图形应用设计。它提供了处理 2D、3D 和 4D 向量以及矩阵运算的高效方法。 主要特性 高性能&#xff1a;经过高度优化&#xff0c;执行速度快轻量级&#xff1a;体积小…...

Spring Boot 核心注解全解:@SpringBootApplication背后的三剑客

大家好呀&#xff01;&#x1f44b; 今天我们要聊一个超级重要的Spring Boot话题 - 那个神奇的主类注解SpringBootApplication&#xff01;很多小伙伴可能每天都在用Spring Boot开发项目&#xff0c;但你真的了解这个注解背后的秘密吗&#xff1f;&#x1f914; 别担心&#x…...

Android 音频架构全解析:从 AudioTrack 到 AudioFlinger

在开发音视频相关应用时&#xff0c;我们常会接触到 MediaPlayer、SoundPool、AudioTrack、OpenSL ES、AAudio、Oboe 等名词&#xff0c;它们都与 Android 的音频播放息息相关。然而&#xff0c;真正理解它们之间的关系以及背后运行机制&#xff0c;才能写出高性能、低延迟的音…...

【教程】无视硬件限制强制升级Windows 11

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、下载升级工具&#xff1a;https://github.com/builtbybel/Flyby11/releases 2、解压后打开软件&#xff1a; 3、拖入win11.iso或者自动下载&#xf…...