Go

Go Mutex 源码浅析 & Mutex 演变历程

TT
文章的内容参考了极客时间专栏《Go 并发编程实战课》,结合自己的理解写了一篇总结。 作者将 Mutex 的演进划分成了 4 个阶段: 初版 使用一个 key 字段标记是否持有锁,以及等待该锁的 goroutine 数量 源码下载地址 部分代码片段如下: package sync import "runtime" // CAS操作,当时还没有抽象出atomic包 func cas(val *uint32, old, new uint32) bool // 互斥锁的结构,包含两个字段 type Mutex struct { key uint32; // 锁是否被持有的标识,0:锁未被持有;1:锁被持有,且没有其它等待者;n:锁被持有,同时还有 n-1 个竞争者 sema uint32; //信号量专用,用以阻塞/唤醒goroutine } // 保证成功在val上增加delta的值 func xadd(val *uint32, delta int32) (new uint32) { for { v := *val; nv := v+uint32(delta); if cas(val, v, nv) { return nv; } } panic("unreached"); } // 请求锁 func (m *Mutex) Lock() { if xadd(&m.

Go go:linkname 是个啥

TT
问题发现 今天阅读 golang 源码时,看到一个函数 runtime_canSpin 大概意思是判断能否进行自旋,想查看它的实现,idea 跳转进去以后在 sync/runtime.go 文件里只看到了函数的定义 // Active spinning runtime support. // runtime_canSpin reports whether spinning makes sense at the moment. func runtime_canSpin(i int) bool 并没有函数的实现,同目录下也没有找到汇编代码的实现,那函数的具体实现在哪里? 找答案 clone 了 golang 源码,全局搜索 runtime_canSpin,在 runtime/proc.go 文件找到了如下的代码块: // Active spinning for sync.Mutex. //go:linkname sync_runtime_canSpin sync.runtime_canSpin //go:nosplit func sync_runtime_canSpin(i int) bool { // sync.Mutex is cooperative, so we are conservative with spinning. // Spin only few times and only if running on a multicore machine and // GOMAXPROCS>1 and there is at least one other running P and local runq is empty.

Go Context

TT
文中例子参考:https://www.flysnow.org/2017/05/12/go-in-action-go-context.html 1. 使用 chan + select 控制 goroutine 停止 package main import ( "fmt" "time" ) func main() { stop := make(chan bool) go func() { for { select { case value1, ok1:= <-stop: fmt.Println(value1, ok1) fmt.Println("监控退出,停止了...1") return default: fmt.Println("goroutine监控中...1") time.Sleep(2 * time.Second) } } }() go func() { for { select { case value2, ok2:= <-stop: fmt.Println(value2, ok2) fmt.Println("监控退出,停止了...2") return default: fmt.Println("goroutine监控中...2") time.Sleep(2 * time.Second) } } }() time.

Go 防缓存击穿利器 - singleflight

TT
1. 需求描述 热点数据保存在缓存中,缓存过期的瞬间,避免大量的请求直接打到数据库服务器上。 2. singleflight 使用 demo package main import ( "errors" "fmt" "golang.org/x/sync/singleflight" "sync" "sync/atomic" ) var ErrNotFund = errors.New("not fund") func main() { var clt = NewCacheClient() wg := sync.WaitGroup{} wg.Add(10) for i := 0; i < 10; i++ { //模拟10个并发请求从缓存获取数据 go func() { defer wg.Done() clt.Get("a") }() } wg.Wait() } type CacheClient struct { keys atomic.Value //实际存储的是 map[string]string group singleflight.Group } func NewCacheClient() *CacheClient { clt := new(CacheClient) m := make(map[string]string) clt.

什么是 CAS

TT
什么是 CAS CAS 的全称是 Compare And Swap. 该指令会将给定的值和一个内存地址中的值进行比较,如果它们是同一个值,就使用新值替换内存地址中的值,整个操作过程是原子性的。 那啥是原子性呢? 原子性我们只需要记住一点,就是这个操作总是基于最新的值进行计算,如果同时有其它线程已经修改了这个值,那么这次操作不成功。 听起来还是很绕?贴个 golang 源码片段(还带汇编的哦) // func cas(val *int32, old, new int32) bool // Atomically: // if *val == old { // *val = new; // return true; // }else // return false; TEXT sync·cas(SB), 7, $0 MOVQ 8(SP), BX MOVL 16(SP), AX MOVL 20(SP), CX LOCK CMPXCHGL CX, 0(BX) JZ ok MOVL $0, 24(SP) RET ok: MOVL $1, 24(SP) RET 再贴一段早期的 Mutex 源码片段 源码地址