Golang 锁源码分析

golang源码分析:sync.Pool 如何从读写加锁到无锁 我们知道,早期的sync.Pool,底层是通过互斥锁实现对共享队列的并发访问的,这里会存在的问题是,尽管是分段锁,高并发场景下频繁进行G的wait和runable状态调度其实开销也不算小 通过互斥锁保证并发安全的sync.Pool数据结构 type Pool struct { noCopy noCopy local unsafe.Pointer // local,固定大小per-P池, 实际类型为 [P]poolLocal localSize uintptr // .. 阅读详情

锁的实现

数据结构

type Mutex struct {
   state int32
   sema  uint32
}
  • 一个mutex 总共只占8个字节,因此是一个充分使用位的数据结构
  • state意义:
    • 最后3位充当状态的概念
      • 第0位: mutexLocked: 表示互斥锁的状态为锁定状态,既是否被某个goroutine所持有,是否已经被加锁
      • 第1位: mutexWoken: 是否被唤醒(既某个goroutine尝试获取锁)
      • 第2位: mutexStarving:表示当前的互斥锁处于 饥饿模式
      • 剩下的则是有多少个goroutine 在等待互斥锁的释放
      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5GY9i2f6-1628645052365)(/Users/joker/Nutstore Files/我的坚果云/复习/imgs/golang_mutx_数据结构.png)]
  • sema意义:
    • 充当信号量:用来唤醒goroutine

正常模式和饥饿模式

  • 正常模式:

    • 锁的等待者会按先进先出的顺序获取锁
      • 唤醒的goroutine不会直接获取锁,而是和新请求的routine竞争获取锁,因为新请求的goroutine 占用着cpu,所以刚刚唤醒的routine大概率会失败竞争
  • 饥饿模式:

    • 锁直接交给goroutine 等待队列中的第一个,而不会触发竞争(即时锁看上去处于unlock状态,依旧不会自旋,而是直接放入等待队列中)

    • 作用:

      • 防止goroutine 被饿死,既因为刚请求的goroutine更大概率获得锁,意味着之前的goroutine 很可能一直获取不到锁

源码分析:

hard code 定义分析

const (
	mutexLocked = 1 << iota // 0001 ,代表的是,这个锁已经被加锁
	mutexWoken  0010 代表的是,是否有routine被唤醒
	mutexStarving    0100 代表的是,当前锁处于饥饿模式
	mutexWaiterShift = iota   等待的goroutine数的位移, 如 stat>>mutexWaiterShift 就能得到当前的wait数

	starvationThresholdNs = 1e6 // 1e6 ns =1ms ,既 如果当前goroutine在1ms 内获取得到锁,并且处于饥饿模式时,将锁
	更正为正常模式
)

一些native 函数解释

  • sync_runtime_canSpin: 自旋
    • 自旋只会自旋几次,4次
    • 运行于多核的机器上
    • 当前机器上至少存在一个正在运行的P,并且处理的运行队列为空
  • runtime.sync_runtime_doSpin
    • 执行PAUSE 指令,空占CPU时间
  • sync_runtime_SemacquireMutex:
    • 通过golang 运行时的信号量,获取信号量
    • lifo:如果为true 则会排在队头,
  • runtime_Semrelease
    • 通过golang的信号量,释放通知
    • handoff: 如果为true,则直接按先进先出的方式,首位直接被唤醒

加锁源码分析

func (m *Mutex) Lock() {
	// 先判断是否已经被加锁,并且是无等待者,是的话,直接设置为加锁即可
	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
		if race.Enabled {
			race.Acquire(unsafe.Pointer(m)) // 这个在test 模式下非常用于,用于查看routine之间竞争
		}
		return
	}
	m.lockSlow()
}
func (m *Mutex) lockSlow() {
	var waitStartTime int64
	starving := false
	awoke := false
	iter := 0
	old := m.state // 拷贝之前的状态
	for {
		// old&(mutexLocked|mutexStarving) == mutexLocked : 如果不处于饥饿模式,并且已经被其他routine所锁定
		// runtime_canSpin(iter) 并且当前次数 未达到自旋次数临界值,则 自旋
		// 这里会有问题: 为什么处于被其他goroutine锁定时也要进行自旋? 原因在于: 正常模式下,会试图抢占锁
		if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
			// !awoke: 表明当前goroutine 未唤醒
			// old&mutexWoken == 0: 当前lock中 没有goroutine 在尝试获取锁
			// old>>mutexWaiterShift 并且 没有goroutine 被阻塞  
			// 最后: 将自己设置为唤醒状态,并且将锁的状态也修改为唤醒
			if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
				atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
				awoke = true
			}
			// 开始自旋
			runtime_doSpin()
			iter++
			old = m.state
			continue
		}
		// 跳出上述的for 循环表明:
		// old&(mutexLocked|mutexStarving) == mutexLocked: 不满足: 既,处于饥饿模式下,或者是 锁已经被人释放了
		// runtime_canSpin(iter): 达到自旋次数最大值
		
		new := old
		// 如果处于饥饿模式下, 不抢占,直接表明为locked (然后把自己丢到排队去)
		if old&mutexStarving == 0 {
			new |= mutexLocked
		}
		// 处于饥饿模式,或者是被抢占了,则直接排队处理
		if old&(mutexLocked|mutexStarving) != 0 {
			new += 1 << mutexWaiterShift
		}

		// 如果在饥饿模式下被抢占了,则更新锁为饥饿锁,也就是说,如果不是饥饿模式或者锁没有被抢占则不会设置为饥饿模式
		if starving && old&mutexLocked != 0 {
			new |= mutexStarving
		}
		// 如果该goroutine被唤醒,要么是获得了锁,要么是处于休眠状态,总之不能是woken状态
		if awoke {
			if new&mutexWoken == 0 {
				throw("sync: inconsistent mutex state")
			}
			// 清除状态
			new &^= mutexWoken
		}
		// cas 交换更新锁状态: 可能只是更新为饥饿模式,不一定代表就是获取得到了锁
		if atomic.CompareAndSwapInt32(&m.state, old, new) {
			 // 如果锁之前是未锁定,且不处于饥饿状态,则表明,我们获得了锁,直接退出
			if old&(mutexLocked|mutexStarving) == 0 {
				break // locked the mutex with CAS
			}
		  
		  // 如果该goroutine为新的goroutine,则queueLifo 为false
		  // 否则为唤醒的goroutine,为 true
			queueLifo := waitStartTime != 0
			if waitStartTime == 0 {
				waitStartTime = runtime_nanotime()
			}
			// 该函数的作用为: 如果是新来的goroutine,放到队尾,否则是唤醒的goroutine,放到队头
			runtime_SemacquireMutex(&m.sema, queueLifo, 1)
			// 判断该goroutine 是否处于饥饿模式 (通过上述的starving 以及时间间隔)
			starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
			old = m.state
			// 如果该锁处于饥饿模式
			if old&mutexStarving != 0 {
				// 那么根据golang锁的定义,如果是饥饿模式下的锁,获取锁的必须是对头(既本goroutine),
				// 也就是说,当该goroutine被唤醒之后,锁不可以被别人给抢占,也不会有其他goroutine被唤醒去抢占
				// 并且因为是饥饿模式,所以当前goroutine肯定是在等待队列中的,因此 old>>mutexWaiterShift 不能为0
				if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
					throw("sync: inconsistent mutex state")
				}
				// 锁等待-1
				delta := int32(mutexLocked - 1<<mutexWaiterShift)
				// 如果当前goroutine是队列中最后一个,则退出饥饿模式
				if !starving || old>>mutexWaiterShift == 1 {
					delta -= mutexStarving
				}
				// 更新state,因为在上面我们已经 - 去了一些状态(如饥饿模式,等待数等),所以直 add即可
				atomic.AddInt32(&m.state, delta)
				break
			}
			awoke = true
			iter = 0
		} else {
			old = m.state
		}
	}

	if race.Enabled {
		race.Acquire(unsafe.Pointer(m))
	}
}
解锁源码分析
  • 注意点
    • lock 可以被其他goroutine unlock
func (m *Mutex) Unlock() {
	if race.Enabled {
		_ = m.state
		race.Release(unsafe.Pointer(m))
	}

	// 先尝试直接解锁,如果为0,表明解锁成功
	
	new := atomic.AddInt32(&m.state, -mutexLocked)
	if new != 0 {
		// Outlined slow path to allow inlining the fast path.
		// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.
		m.unlockSlow(new)
	}
}
func (m *Mutex) unlockSlow(new int32) {
// 解锁的前提是加锁,如果加锁没有,则是一个错误的状态,直接抛异常
	if (new+mutexLocked)&mutexLocked == 0 {
		throw("sync: unlock of unlocked mutex")
	}
	// 如果处于饥饿模式下
	if new&mutexStarving == 0 {
		old := new
		for {
			// 如果没有goroutine在抢占锁了,或者是
			if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
				return
			}
			// Grab the right to wake someone.
			new = (old - 1<<mutexWaiterShift) | mutexWoken
			if atomic.CompareAndSwapInt32(&m.state, old, new) {
			// 调用 sema.go  唤醒一个goroutine 
				runtime_Semrelease(&m.sema, false, 1)
				return
			}
			old = m.state
		}
	} else {
		// 在饥饿模式下,handoff为true,直接唤醒的是第一个goroutine 
		// 参数handoff true 代表着直接唤醒等待队列goroutine中的第一个, 被唤醒的第一个,基于饥饿模式下,会直接获得锁
		// 而新来的goroutine会进到队尾
		runtime_Semrelease(&m.sema, true, 1)
	}
}


读写锁的实现

  • What:
    • 并发读之间不互斥
    • 并发写之间互斥
    • 并发读和写互斥
数据结构
type RWMutex struct {
	w           Mutex  // 既写锁,用于复用
	writerSem   uint32 // 写等待读的数
	readerSem   uint32 // 读等待写的数
	readerCount int32  // 当前正在读的goroutine数
	readerWait  int32  // 当写操作时,读等待的数
}
加读锁
func (rw *RWMutex) RLock() {
	// debug 模式下启用有很大好处
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	// readerCount+1 ,如果readerCount<0 则认为是被写锁占用着,此时goroutine 等待阻塞被唤醒
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		runtime_SemacquireMutex(&rw.readerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}
解读锁
func (rw *RWMutex) RUnlock() {
	if race.Enabled {
		_ = rw.w.state
		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
		race.Disable()
	}
	
	// 如果 >=0 ,说明解锁成功
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		// Outlined slow-path to allow the fast-path to be inlined
		rw.rUnlockSlow(r)
	}
	if race.Enabled {
		race.Enable()
	}
}

func (rw *RWMutex) rUnlockSlow(r int32) {
	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
		race.Enable()
		throw("sync: RUnlock of unlocked RWMutex")
	}
	// 如果有写操作在等待,则释放信号量,让写操作尝试竞争获取锁
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		// The last reader unblocks the writer.
		runtime_Semrelease(&rw.writerSem, false, 1)
	}
}
加写锁
func (rw *RWMutex) Lock() {
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	// 可能有多个routine在加锁,则复用之前的锁机制代码
	rw.w.Lock()
	// Announce to readers there is a pending writer.
	// 将readerCount 剪到最小(所以在加读锁或者解读锁的时候可以通过这个值来判断是否有写routine)
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders	
	// 将读操作等待的routine数追加到 写操作等待读的数 ,不为0 ,所有当前有读锁,则等待信号量的获取
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
		race.Acquire(unsafe.Pointer(&rw.writerSem))
	}
}
解写锁
func (rw *RWMutex) Unlock() {
	if race.Enabled {
		_ = rw.w.state
		race.Release(unsafe.Pointer(&rw.readerSem))
		race.Disable()
	}

	// 先还原当加锁之前有多少个读routine在等待读取
	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
	if r >= rwmutexMaxReaders {
		race.Enable()
		throw("sync: Unlock of unlocked RWMutex")
	}
	// 因为写锁被占用之后,所有的读routine都被阻塞,所以要挨个唤醒
	for i := 0; i < int(r); i++ {
		runtime_Semrelease(&rw.readerSem, false, 0)
	}
	// 可能此时还有其他的写锁routine 在尝试获取锁,则 转交给mutex操作
	rw.w.Unlock()
	if race.Enabled {
		race.Enable()
	}
}

总结

  • 读写锁

    • 读锁:
      • 加读锁的时候,会先判断写操作是否已经占用,是的话,则会等待信号量获取
      • 解读锁的时候,同样的也是基于readCount是否<0 ,小于0则会去尝试唤醒写routine
    • 写锁:
      • 加写锁的时候,会先交给写锁routine之间竞争获取,然后再判断是否有读routine,有的话则阻塞,否则的话,设置状态表明有写routine了
      • 解写锁的时候,会先唤醒所有因为该写锁而阻塞的读routine,然后再交给mutex 释放写锁(因为期间可能还有其他写锁在)
  • golang的锁有2种模式

    • 正常模式和饥饿模式
    • 正常模式,gorputine之间会竞争获取锁,饥饿模式下,排队的形式获取锁
    • 当饥饿模式下,如果刚好最后一个goroutine获得锁,或者是获取锁的时间间隔在1ms内,则会恢复为正常模式
    • golang的锁充分运用位运算,低3位代表了 是否被锁定,是否有routine唤醒,以及锁的状态,剩下的位数则是routine的等待数
    • goroutine的唤醒与阻塞都是基于golang 的信号量机制实现

问题

  • 读写锁:

    • 写锁饥饿怎么处理,既 n个读routine,1个写routine
      • A: golang的读解锁的时候,会尝试唤醒写锁的routine,如果有写等待的话
    • 为什么读加锁,如果readerCount<0 就被阻塞
      • A: 因为 golang 的读写锁加了写锁之后,会将readCount 会赋值一个 增量的负值来表明 有写routine了,而读是需要等待写的(当然写也要等待读)
    • 为什么读解锁,如果readerCount>=0 就认为解锁成功
      • a. golang的读写锁是可以被其他routine给解开的
      • b. readerCount<0的情况为,只有当写锁抢占的时候会<0 ,所以是要阻塞等待的
    • 如何防止 读解锁之前是没有被RLock的
      • 是通过 内部的readerCount 判断的,readerCount 是当前正在读的goroutine数,没RLock表明为0 或者是被写锁 -maxRead ,
  • 什么是PAUSE指令

    • 使得进程(线程)挂起,直到收到信号,且信号函数返回成功,pause函数才会返回
  • 解锁问题

    • 为什么直接-mutexLocked 然后判断结果==0 就可以判断是否解锁成功,不为0的情况有哪些

    • 为什么饥饿模式下,唤醒的时候,不需要判断是否

  • 从正常模式到饥饿模式的触发条件

    • starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
    • **既当自旋时间超过了starvationThresholdNs 就会进入饥饿模式, starvationThresholdNs 为1e6 纳秒,既1ms **
  • 从饥饿模式恢复到正常模式的条件

  • 如果该goroutine获取得到了锁,并且满足下面任意一个条件都会转为正常竞争模式

    • 该goroutine为队列中最后一个
    • 等待的时间小于1ms
go 并发模型 之 sync -- Mutex (四) 前言 前面的内存模型的章节里讲到 内存屏障 barrier 指令要求所有对内存的操作都必须要“扩散”到 memory 之后才能继续执行其他对 memory 的操作。 因此,我们可以用高级点的 atomic compare-and-swap,或者直接用更高级的锁,通常是标准库提供。 一、实现原理 实现模式 Barging., Handsoff, Spinning Barging:这种模式是为了提高吞吐量,当锁被释放时,它会唤醒第一个等待者,然后把锁给第一个... 阅读详情

相关推荐

sync|Mutex、RWMutex源码分析

饥饿模式对于阻止某个goroutine被饿死有重要意义[饥饿模式是计算机科学和工程领域的一个概念,指的是某些进程或任务由于不断被其他优先级更高的进程抢占或延迟而无法取得进展或完成的情况。对于RLock的调用,对于任何对RLock的调用,都存在一个n,使得对Unlock的第n次调用在对RLock的调用之前“同步”,并且对应的对RUnlock的调用在对Lock的第n+1次调用之前“同步”。正常模式,锁等待者按照先入先出的顺序排队,但是一个被唤醒的锁等待者并没有真正的拥有锁,需要和新到来的获取锁者进行竞争。

weixin_46290302的博客 1035

Go 互斥锁 Mutex 源码分析(二)

原创文章,欢迎转载,转载请注明出处,谢谢。 0. 前言 在 Go 互斥锁 Mutex 源码分析(一) 一文中分析了互斥锁的结构和基本的抢占互斥锁的场景。在学习锁的过程中,看的不少文章是基于锁的状态解释的,个人经验来看,从锁的状态出发容易陷入细节,了解锁的状态转换过一段时间就忘,难以做到真正的理解。想来是用静态的方法分析动态的问题导致的。在实践中发现结合场景分析互斥锁对笔者来说更加清晰,因此有了...

weixin_42594427的博客 210

解析Golang领域RWMutex的设计思路

本文旨在深入分析Golang标准库中RWMutex的设计思路和实现细节。读写锁的基本概念及其在并发编程中的作用Golang RWMutex的具体实现机制设计中的性能考量和优化策略实际应用中的最佳实践和常见陷阱文章首先介绍读写锁的基本概念,然后深入分析Golang RWMutex的实现,包括其状态管理、锁获取和释放策略。接着通过实际代码示例展示其使用方法,最后讨论性能优化和应用场景。RWMutex: 读写互斥锁,允许多个读操作或单个写操作同时进行临界区: 需要同步保护的代码区域锁竞争。

AI Agent Harness Engineering 1073

【Go面试题】Mutex

Golang的并发编程

chenkeng99的博客 553

手把手教你:基于STM32H7自制飞控,如何从零开始移植PX4固件(附避坑指南)

本文详细介绍了如何基于STM32H7自制飞控板并移植PX4固件的完整流程,包括硬件设计、开发环境搭建、关键配置文件移植、驱动开发与适配等关键步骤。特别针对PX4-AutoPilot的移植过程提供了实用技巧和避坑指南,帮助开发者高效完成飞控系统开发。

weixin_30271335的博客 143

[Linux外设驱动详解]RK3588 U-Boot网络功能深度分析

本文深入分析了RK3588平台上U-Boot的网络功能实现,主要内容包括: 网络架构:详细剖析了从硬件层到协议层的完整网络栈架构,包括GMAC驱动、TFTP/NFS启动、网络命令等关键组件。 驱动实现:重点介绍了RK3588 GMAC驱动结构,包含关键数据结构、专用操作函数集、设备树兼容性配置和GRF寄存器设置。 功能实现:详细说明了驱动初始化流程、RGMII速度适配机制以及PHY接口配置过程。 高级特性:涵盖了网络启动功能、镜像更新和MAC地址传递到Linux内核的机制。 文档通过代码片段和流程图清晰展示

Just Linux! 1113

golang mutex源码详细解析

  目前golang的版本是1.12,其中的mutex是增加了普通模式和饥饿模式切换的优化版本,为了便于理解,这里先从上一个版本1.7版本的mutex开始分析,以后再对优化版本进行说明。 1.7版本Mutex实现 Mutex定义 // mutex是互斥锁 // mutex的零值是没有加锁的 //在使用之后不能被拷贝 type Mutex struct { state int32 //状态...

void 1089

STM32实现任意角度移相全桥PWM

STM32实现任意角度移相全桥PWM,最小分辨率0.5度,频率占空比课任意调节 本文重点在于如何产生任意角度的移相, 两个定时器工作于主从模式,从模式为Gate mode,用第一个定时器触发第二个来实现延时移相

Wind blog 3万+

Golang的mutex源码阅读

/ atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken):使用原子操作CAS(Compare-And-Swap)修改互斥锁的状态,将原来的状态(old)与mutexWoken(唤醒标志)进行或运算,设置唤醒标志,同时返回操作是否成功的布尔值。//总之,runtime_SemacquireMutex(&m.sema, queueLifo, 1) 是实现Mutex的加锁操作的核心代码,//等待Mutex的解锁。//用于表示等待队列的先进先出原则。

watestill的博客 795

零基础学GPU KMD--8. 内存管理:GPU 显存与系统内存的协同​8.1 显存分配策略:GEM / TTM 原理

支持 VRAM、GTT(Graphics Translation Table)、SYSTEM 内存的动态迁移与缓存管理。:若缓冲区在 SYSTEM,TTM 分配 GTT 条目并建立页表映射,使 GPU 可直接访问。:系统内存中预留给 GPU 直接访问的区域,通过 GPU 的 MMU 做地址翻译。你需要我继续展开这一部分吗?​ 为复杂离散 GPU 设计,应对大容量 VRAM、多内存域、显存迁移等问题。:重量级、功能全,适合大容量、多内存域的离散 GPU,强调显存迁移与统一管理。

amy_mhd的博客 185

基于A*算法的二维栅格地图路径规划与避障MATLAB仿真

本文将介绍如何使用MATLAB编写基于A*算法的二维栅格地图路径规划与避障的仿真程序。我们可以使用二维数组表示地图,其中每个元素表示一个栅格的状态,例如1表示障碍物,0表示可行走区域。接下来,我们需要定义A*算法的节点类。G值表示从起始节点到当前节点的代价,H值是启发式函数估计的从当前节点到目标节点的代价。算法,我们可以有效地找到起始点到目标点的最短路径,并在仿真中进行可视化展示。算法的基本思路是从起始节点开始,按照一定的启发式函数选择下一个节点,直到找到目标节点或者遍历完所有可行走区域。

ByteWhizX的博客 1273

信号量,锁和 golang 相关源码分析

本文为社区粉丝原创投稿,再次感谢作者南瓜waniu的分享,欢迎大家在评论区留言和作者讨论,同时也欢迎大家踊跃投稿,分享您的golang语言学习经验!投稿邮箱地址为tougao@golan...

QQ1528884535的专栏 359

第16课:动手实战中文句法依存分析

句法分析是自然语言处理(NLP)中的关键技术之一,其基本任务是确定句子的句法结构或者句子中词汇之间的依存关系。主要包括两方面的内容:一是确定语言的语法体系,即对语言中合法句子的语法结构给予形式化的定义;另一方面是句法分析技术,即根据给定的语法体系,自动推导出句子的句法结构,分析句子所包含的句法单位和这些句法单位之间的关系。 句法分析被用在很多场景中,比如搜索引擎用户日志分析和关键词识别,比如信息抽...

春华秋实 2435

golang mutex 锁原理 、源码分析

本文介绍了 mutex 锁 两种模式:正常模式、饥饿模式 以及 两章模式的切换条件 最后将对 mutex 源码进行分析 一、 mutex 锁 模式及运行原理 二、mutex 锁 源码分析 未完待续......

demongaobo的博客 462

YOLOv13添加轻量化的EMA模块Faster_EMA,轻量又涨点!map50提升4.56个点,召回率提升9.66!

本文介绍了YOLOv13模型的优化移植方法,通过添加Faster_EMA模块和超参数调整,使模型性能显著提升。主要修改包括:1) 创建新的模型配置文件yolov13-Faster_EMA.yaml;2) 在tasks.py中添加新模块;3) 修改__init__.py和block.py文件,新增Partial_conv3和DropPath类。优化后模型mAP50提升4.56%,召回率提升9.66%,验证了改进方案的有效性。代码实现了局部卷积和随机深度等先进技术,为YOLO系列模型性能优化提供了新思路。

FJN110的博客 2604

OpenClaw进阶实战(二十五):抖音企业号——私信回复与视频评论分析

本文介绍了抖音企业号自动化运营的技术方案,重点解决私信互动效率低和风控严格两大挑战。文章分析了抖音集成的三大技术壁垒(视频复杂性、风控体系、封闭API)并对比了三种技术路径的优缺点。针对不同规模的运营主体,推荐官方API+RPA混合架构或真机RPA+AI辅助的方案。 详细讲解了环境准备步骤,包括抖音Skill安装、Cookie获取和配置文件设置。核心实现部分展示了私信自动回复功能的安全合规实现,包含意图分类、优先级排序、延迟发送等防封禁机制,并提供了完整的Python代码示例。该方案通过模拟人工操作、限制日

PeterPan的博客 159

golang 全局变量_信号量,锁和 golang 相关源码分析

本文为社区粉丝原创投稿,再次感谢作者南瓜waniu的分享,欢迎大家在评论区留言和作者讨论,同时也欢迎大家踊跃投稿,分享您的golang语言学习经验!投稿邮箱地址为tougao@golang.ltd原创投稿:南瓜waniu1资源同步1.1 解决方案2 信号量2.1 共享变量2.2 信号量3 锁3.1 死锁3.2 活锁3.3 饥饿4 Golang sync 包4.4.1 数据结构4.4.2 ...

weixin_39652646的博客 352

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

判断sem信号量为零_信号量,锁和 golang 相关源码分析

原标题:信号量,锁和 golang 相关源码分析本文为社区粉丝原创投稿,再次感谢作者南瓜waniu的分享,欢迎大家在评论区留言和作者讨论,同时也欢迎大家踊跃投稿,分享您的golang语言学习经验!投稿邮箱地址为tougao@golang.ltd原创投稿:南瓜waniu1资源同步1.1 解决方案2 信号量2.1 共享变量2.2 信号量3 锁3.1 死锁3.2 活锁3.3 饥饿4 Golang syn...

weixin_35897853的博客 405

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

k8s 分布式资源锁 + 守护进程

我在写一个部署到k8s的守护进程时,同事说要写一个锁,防止起多个pod时多个守护进程都干活,所以需要分布式资源锁。 [k8s源码分析][client-go] k8s选举leaderelection (分布式资源锁实现) golang 守护进程 ...

wowenlong的博客 998

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

这不会又是一个Go的BUG吧?

hello,大家好呀,我是小楼。最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点在于Java和Go的读写锁对比,甚至看完后你会有一个隐隐的感觉:Go的读写锁是不是有BUG?故障回放 背景简单抽象一下:一个server服务(Go语言实现),提供了一个http接...

Go中国 189

这可能是最容易理解的 Go Mutex 源码剖析

上一篇文章《一文完全掌握 Go math/rand》,我们知道 math/rand 的 global rand 有一个全局锁,我的文章里面有一句话:“修复方案: 就是把 rrRand 换成了 globalRand, 在线上高并发场景下, 发现全局锁影响并不大.”, 有同学私聊我“他们遇到线上服务的锁竞争特别激烈”。确实我这句话说的并不严谨。但是也让我有了一个思考:到底多高的 QPS 才能让 Mutex 产生强烈的锁竞争? 到底加锁的代码会不会产生线上问题?到底该不该使用锁来实现这个功能?线上的问题是不是由

聪明的竹子的博客 600

go mutex.go 中 lock unlock 源码解析

go mutex.go 中 lock unlock 源码解析 mutex 主要是根据锁状态已经信号量来做锁的优化以及锁的升级 // 加锁的逻辑 type Mutex struct { state int32 sema uint32 } const ( mutexLocked = 1 << iota // mutex is locked mutexWoken mutexStarving mutexWaiterShift = iota // 3 )

aaqq123456654321的博客 477

源码分析-Golang Mutex

锁的实现 数据结构 type Mutex struct { state int32 sema uint32 } 一个mutex 总共只占8个字节,因此是一个充分使用位的数据结构 state意义: 最后3位充当状态的概念 第0位: mutexLocked: 表示互斥锁的状态为锁定状态,既是否被某个goroutine所持有,是否已经被加锁 第1位: mutexWoken: 是否被唤醒(既某个goroutine尝试获取锁) 第2位: mutexStarving:表示当前的互斥锁处于 饥

Coder_Joker的博客 joke a joker 311

Go 中的锁源码实现:Mutex

上一篇文章《当我们谈论锁,我们谈什么》 中我提到了锁,准确地说是信号量(semaphore, mutext是semaphore的一种)的实现方式有两种:wait的时候忙等待或者阻塞自己。1234567891011121314//忙等待wait(S) {    while(S&lt;=0)        ;   //no-op    S--}//阻塞wait(semaphore *S) {    S...

嘿嘿 1812

golang 重要知识:waitgroup 解析

Golang 提供了简洁的 go 关键字来让开发者更容易的进行并发编程,同时也提供了 WaitGroup 对象来辅助并发控制。今天我们就来分析下 WaitGroup 的使用方法,顺便瞧一瞧它的底层源码

yue_xin_tech 1949
上一篇: jmeter docker 分布式压测
下一篇: Talk Is Cheap,Show Me The Code-GoLang动态监听chan
Coder_Joker
博客等级 码龄8年 39粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值