Closed draveness closed 2 years ago
你这都出书了.....
你这都出书了.....
这也算出书么...
开源电子书😂
老哥 厉害
lockSlow
是你提出来的,还是源码封装好的。
lockSlow
是你提出来的,还是源码封装好的。
源码封装好的
你好,想问下,cond的listen方法,看代码第一个协程获取锁后,调用wait方法等待。其他协程并不能获取到锁,也无法调用wait方法,是都被阻塞了吗
@wenhemin 你好,想问下,cond的listen方法,看代码第一个协程获取锁后,调用wait方法等待。其他协程并不能获取到锁,也无法调用wait方法,是都被阻塞了吗
是的,只能通过 Signal 和 Broadcast 唤醒锁,Wait 就是等待信号
哇塞 这就是我想要的 一站式搞完 6666
mutex的unlock 为什么要这么设计 ? 可以让别人unlock,然后自己再调用的花 就panic了 ?https://play.golang.org/p/k3qHjbwOFtF
mutex的unlock 为什么要这么设计 ? 可以让别人unlock,然后自己再调用的花 就panic了 ?https://play.golang.org/p/k3qHjbwOFtF
这个跟是不是其他线程解锁没关系吧,是不能解锁已经处于 unlocked
状态的互斥锁
mutex的unlock 为什么要这么设计 ? 可以让别人unlock,然后自己再调用的花 就panic了 ?https://play.golang.org/p/k3qHjbwOFtF
这个跟是不是其他线程解锁没关系吧,是不能解锁已经处于
unlocked
状态的互斥锁
嗯 是的 听你这么说 没毛病,我把 锁的持有者 搞错了。 大佬,你再看下我之前在你Goroutine调度文章里面回复你的, 为什么 周期性 判断 %61==0 ,从global running G queue 里面取G 的问题?
sync.Mutex.unlockSlow
部分有一处笔误:如果互斥锁不存在等待者或者互斥锁的
mutexLocked
、mutexStarving
、mutexWoken
状态都为 0,那么当前方法就可以直接返回,不需要唤醒其他等待者;
按照原代码,应为 不都为 0。
sync.noCopy
处的例子不太恰当:sync.noCopy 是一个特殊的私有结构体,tools/go/analysis/passes/copylock 包中的分析器会在编译期间检查被拷贝的变量中是否包含 sync.noCopy 结构体,如果包含该结构体就会在运行时报出以下错误:
func main() {
wg := sync.Mutex{}
yawg := wg
fmt.Println(wg, yawg)
}
$ go run proc.go
./prog.go:10:10: assignment copies lock value to yawg: sync.Mutex
./prog.go:11:14: call of fmt.Println copies lock value: sync.Mutex
./prog.go:11:18: call of fmt.Println copies lock value: sync.Mutex
因为,
sync.Mutex
中并不包含 sync.noCopy
结构体。Lock
和 Unlock
方法。go vet proc.go
。2020-10-19 UPDATES: 增加了 lockType 相关的判断
func main() {
wg := sync.Mutex{}
yawg := wg
fmt.Println(wg, yawg)
}
这个编译器不会给报错,只会有警告。 同意楼上,你应该执行的是
go vet proc.go
func main() {
wg := sync.Mutex{}
yawg := wg
fmt.Println(wg, yawg)
}
$ go run proc.go
这里的Mutex应该是WaitGroup吧 https://play.golang.org/p/j2n0gJghU9s
func main() { wg := sync.Mutex{} yawg := wg fmt.Println(wg, yawg) }
这个编译器不会给报错,只会有警告。 同意楼上,你应该执行的是
go vet proc.go
已修复
func main() { wg := sync.Mutex{} yawg := wg fmt.Println(wg, yawg) } $ go run proc.go
这里的Mutex应该是WaitGroup吧 https://play.golang.org/p/j2n0gJghU9s
已修复
@draveness
func main() { wg := sync.Mutex{} yawg := wg fmt.Println(wg, yawg) }
这个编译器不会给报错,只会有警告。 同意楼上,你应该执行的是
go vet proc.go
已修复
func main() { wg := sync.Mutex{} yawg := wg fmt.Println(wg, yawg) } $ go run proc.go
这里的Mutex应该是WaitGroup吧 https://play.golang.org/p/j2n0gJghU9s
已修复
下面的报错信息,没有改过来 ./proc.go:10:10: assignment copies lock value to yawg: sync.WaitGroup contains sync.noCopy ./proc.go:11:14: call of fmt.Println copies lock value: sync.WaitGroup contains sync.noCopy ./proc.go:11:18: call of fmt.Println copies lock value: sync.WaitGroup contains sync.noCopy
如果返回控制(空) — 所有 Goroutine 都成功执行;
如果返回控制(空) — 所有 Goroutine 都成功执行;
修复了
会不会发生第二个lock的永远获取不到锁的情况呢?永远在等在信号量,如果从第三个开始每个都可以把mutexWoken设置为1(当然是符合条件的情况下),那么第二个是不是永远都获取不到锁
会不会发生第二个lock的永远获取不到锁的情况呢?永远在等在信号量,如果从第三个开始每个都可以把mutexWoken设置为1(当然是符合条件的情况下),那么第二个是不是永远都获取不到锁
这种问题不会出现吧,你能说一下出现这种问题时,多个线程的执行的指令顺序么
点赞
建议博主改改 sync.Cond 的例子,既然是条件变量,条件在哪呢?源码注释里的例子就很好嘛
2020-10-21 UPDATES:修改了 sync.Cond 例子,引入了条件
+var status int64
+
func main() {
c := sync.NewCond(&sync.Mutex{})
for i := 0; i < 10; i++ {
go listen(c)
}
- time.Sleep(1*time.Second)
+ time.Sleep(1 * time.Second)
go broadcast(c)
ch := make(chan os.Signal, 1)
@@ -589,13 +591,16 @@ func main() {
func broadcast(c *sync.Cond) {
c.L.Lock()
+ atomic.StoreInt64(&status, 1)
c.Broadcast()
c.L.Unlock()
}
func listen(c *sync.Cond) {
c.L.Lock()
- c.Wait()
+ for atomic.LoadInt64(&status) != 1 {
+ c.Wait()
+ }
fmt.Println("listen")
c.L.Unlock()
}
@wenhemin 你好,想问下,cond的listen方法,看代码第一个协程获取锁后,调用wait方法等待。其他协程并不能获取到锁,也无法调用wait方法,是都被阻塞了吗
是的,只能通过 Signal 和 Broadcast 唤醒锁,Wait 就是等待信号
这个并不一定吧;Wait 方法里面有 Unlock 和 Lock 的间隙在的,这个间隙里其他协程为什么不能获取锁?
func main() {
c := sync.NewCond(&sync.Mutex{})
var ready bool
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Println("Ready: ", i)
c.L.Lock()
defer c.L.Unlock()
fmt.Println("heheh")
for !ready {
c.Wait()
}
fmt.Println("Go: ", i)
}(i)
}
go func() {
c.L.Lock()
defer c.L.Unlock()
time.Sleep(4 * time.Second)
fmt.Println("Now ready")
ready = true
c.Broadcast()
}()
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
<-ch
}
heheh
Ready: 5
Ready: 3
Ready: 4
Ready: 8
Ready: 6
Ready: 7
Ready: 9
heheh
heheh
heheh
heheh
heheh
heheh
Ready: 2
Now ready
Go: 3
heheh
Go: 9
heheh
Go: 2
Go: 4
Go: 8
Go: 6
Go: 7
Go: 1
Go: 0
Go: 5
建议博主改改 sync.Cond 的例子,既然是条件变量,条件在哪呢?源码注释里的例子就很好嘛
下一次 Review 的时候我看一下改不改
这个并不一定吧;Wait 方法里面有 Unlock 和 Lock 的间隙在的,这个间隙里其他协程为什么不能获取锁?
能详细解释一下么,没太看明白你的问题
建议博主改改 sync.Cond 的例子,既然是条件变量,条件在哪呢?源码注释里的例子就很好嘛
下一次 Review 的时候我看一下改不改
这个并不一定吧;Wait 方法里面有 Unlock 和 Lock 的间隙在的,这个间隙里其他协程为什么不能获取锁?
能详细解释一下么,没太看明白你的问题
嗯,我是针对这个问题的提问者那个问题说的,他的问题是:
你好,想问下,cond的listen方法,看代码第一个协程获取锁后,调用wait方法等待。其他协程并不能获取到锁,也无法调用wait方法,是都被阻塞了吗
我的答案是其他协程可以获取到锁,因为 Wait 方法里面如果条件不满足会把当前 goroutine 添加到通知列表,然后执行了 c.L.Unlock
,接着 gopark 当前 goroutine,所以这时其他 goroutine 是可以获取 c.L.Lock()
的,而不是说其他 goroutine 无法调用 Wait 方法。
我的答案是其他协程可以获取到锁,因为 Wait 方法里面如果条件不满足会把当前 goroutine 添加到通知列表,然后执行了
c.L.Unlock
,接着 gopark 当前 goroutine,所以这时其他 goroutine 是可以获取c.L.Lock()
的,而不是说其他 goroutine 无法调用 Wait 方法。
可以的,手动点赞,这节写的有点久了,上下文有点丢失...
我的答案是其他协程可以获取到锁,因为 Wait 方法里面如果条件不满足会把当前 goroutine 添加到通知列表,然后执行了
c.L.Unlock
,接着 gopark 当前 goroutine,所以这时其他 goroutine 是可以获取c.L.Lock()
的,而不是说其他 goroutine 无法调用 Wait 方法。可以的,手动点赞,这节写的有点久了,上下文有点丢失...
嗯,博主写的很不错,我学到好多东西,加油~
简单易懂,感谢
没有找到pool的身影。。
singleflight.Group 这个会抑制多久同样的请求,同一时刻还是全部生命周期的
没有找到pool的身影。。
之后会补上的
singleflight.Group 这个会抑制多久同样的请求,同一时刻还是全部生命周期的
对于这个结构体来说是永久的
@draveness
没有找到pool的身影。。
之后会补上的
对啊,期待有pool相关的文章,很多现代框架或者高性能服务开发都会用到pool,虽然光看官方文档api并不复杂,但是也很想看看更多的细节。
@douglarek
建议博主改改 sync.Cond 的例子,既然是条件变量,条件在哪呢?源码注释里的例子就很好嘛
下一次 Review 的时候我看一下改不改
这个并不一定吧;Wait 方法里面有 Unlock 和 Lock 的间隙在的,这个间隙里其他协程为什么不能获取锁?
能详细解释一下么,没太看明白你的问题
嗯,我是针对这个问题的提问者那个问题说的,他的问题是:
你好,想问下,cond的listen方法,看代码第一个协程获取锁后,调用wait方法等待。其他协程并不能获取到锁,也无法调用wait方法,是都被阻塞了吗
我的答案是其他协程可以获取到锁,因为 Wait 方法里面如果条件不满足会把当前 goroutine 添加到通知列表,然后执行了
c.L.Unlock
,接着 gopark 当前 goroutine,所以这时其他 goroutine 是可以获取c.L.Lock()
的,而不是说其他 goroutine 无法调用 Wait 方法。
确实,乍一看不去看Wait的源码,第一感觉是其他goroutine都会被block。其实wait就是解锁自己,然后让自己陷入休眠并等待被唤醒。被唤醒,可以是broadcast也可以signal。被唤醒之后,在加锁继续执行,执行完后,在这里就是打印,然后解锁。
请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
f()
}
另外一个问题是那个singleflight.Group
,如何做缓存呢?
我想了一下,一般go服务里的每个请求都在一个独立的goroutine
中,我们显然不能在每个goroutine
中都新建一个singlefilght.Group
对象,因为这样一点卵用都没有,每个group里的map,wg都只在这个请求里。
如果做成全局的,那么Do里的那个g.mu.Lock
将会把这个服务上的所有的请求,都给锁死,都得等一个执行时间较长的请求返回。
如何解决这个问题呢?
@zpng singleflight.Group 这个会抑制多久同样的请求,同一时刻还是全部生命周期的
要看第一个进去的请求什么时候执行成功返回结果了。
请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
~感觉 CAS 会比 Load + Store 慢一些,你可以测一下~,看 https://github.com/draveness/blog-comments/issues/151#issuecomment-667944126
如果做成全局的,那么Do里的那个g.mu.Lock 将会把这个服务上的所有的请求,都给锁死,都得等一个执行时间较长的请求返回。
不会锁死所有请求,会锁定相同的请求
@KylinGu 请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
注释说得很清楚:
Do guarantees that when it returns, f has finished.
This implementation would not implement that guarantee:
given two simultaneous calls, the winner of the cas would
call f, and the second would return immediately, without
waiting for the first's call to f to complete.
This is why the slow path falls back to a mutex, and why
the atomic.StoreUint32 must be delayed until after f returns.
@tidyoux
@KylinGu 请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
注释说得很清楚:
Do guarantees that when it returns, f has finished. This implementation would not implement that guarantee: given two simultaneous calls, the winner of the cas would call f, and the second would return immediately, without waiting for the first's call to f to complete. This is why the slow path falls back to a mutex, and why the atomic.StoreUint32 must be delayed until after f returns.
@tidyoux
@KylinGu 请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
注释说得很清楚:
Do guarantees that when it returns, f has finished. This implementation would not implement that guarantee: given two simultaneous calls, the winner of the cas would call f, and the second would return immediately, without waiting for the first's call to f to complete. This is why the slow path falls back to a mutex, and why the atomic.StoreUint32 must be delayed until after f returns.
好吧,我还是看代码理解吧。 可是为啥不能用cas实现呢?cas也同样能实现只做一次,而且其他的调用方也不用block在那里。效率不是更高吗?
@draveness
如果做成全局的,那么Do里的那个g.mu.Lock 将会把这个服务上的所有的请求,都给锁死,都得等一个执行时间较长的请求返回。
不会锁死所有请求,会锁定相同的请求
奥是,重看了一遍代码,见笑了😃,确实是锁相同的请求。感谢~
如果是全局的singleflight.Group对象,仍然存在所有请求共用一把锁的情况呢。每个请求进来,都先抢一下锁,才能执行后续的操作,后面执行完成还要加锁解锁。
所以想请教,正式生产中如何使用这种东西呢?是要把请求分类,设置不同的singleflight吗?
每次 sync.RWMutex.RUnlock 都会将 readerWait 其减一,当它归零时该 Goroutine 就会获得写锁;
这里是不是写错了,应该是readercount减一?
2020-09-01 UPDATED: 已修复
runtime_SemacquireMutex(&m.sema, queueLifo, 1)
starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
old = m.state
if old&mutexStarving != 0 {
if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
throw("sync: inconsistent mutex state")
}
delta := int32(mutexLocked - 1<<mutexWaiterShift)
if !starving || old>>mutexWaiterShift == 1 {
delta -= mutexStarving
}
atomic.AddInt32(&m.state, delta)
break
}
awoke = true
iter = 0
}
如果被runtime_SemacquireMutex唤醒了,什么情况下if old&mutexStarving != 0 这个条件又不成立了?
mutexWoken — 表示从正常模式被从唤醒;
这里表述不是很清楚,我对mutexWoken
的理解是“用于表示当前有被唤醒/新来的goroutine竞争锁“
@longyue0521
mutexWoken — 表示从正常模式被从唤醒;
这里表述不是很清楚,我对mutexWoken
的理解是“用于表示当前有被唤醒/新来的goroutine竞争锁“
是认为 Mutex 可以直接从饥饿模式转到 mutexWoken
么,可以找下相关的代码么
在释放写锁的过程中,第一步应该是少写了一个变量吧
原话: 通过函数atomic.Addint32将变成正数,释放读锁
我觉得应该修改成:通过函数atomic.Addint32将readercount变成正数,释放读锁
2020-10-19 UPDATES: 已作出如下修改
-1. 调用 [`atomic.AddInt32`](https://github.com/golang/go/blob/ca7c12d4c9eb4a19ca5103ec5763537cccbcc13b/src/sync/atomic/doc.go#L93) 函数将变回正数,释放读锁;
+1. 调用 [`atomic.AddInt32`](https://github.com/golang/go/blob/ca7c12d4c9eb4a19ca5103ec5763537cccbcc13b/src/sync/atomic/doc.go#L93) 函数将 `readerCount` 变回正数,释放读锁;
// atomic_amd64
func Load64(ptr *uint64) uint64 {
return *ptr
}
博主你好,最近在看go的源码,但看到atomic.Load这个实现感到有些疑惑,想知道:
Once.Do 展开了
if atomic.LoadUint32(&o.done) == 0 {
// Outlined slow-path to allow inlining of the fast-path.
// o.doSlow(f)
{
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
}
这里使用mutex 有锁的双重检查的味道,为什么还需要使用原子操作呢?
@KylinGu 请教一下大神,请问sync.Once里的Do为什么不能用以下的方式实现,看源码注释有点没看懂。 LoadUint32和StoreUint32有什么区别呢?
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
这个函数保证 Do在f() 执行后才返回,使用CAS 保证不了
https://draveness.me/golang-sync-primitives