blog
blog copied to clipboard
Go sync.Cond | Go 语言高性能编程 | 极客兔兔
https://geektutu.com/post/hpg-sync-cond.html
Go 语言/golang 高性能编程,Go 语言进阶教程,Go 语言高性能编程(high performance go)。sync.Cond 是一个条件锁,也被称为条件变量,常用来一写多读(一个 goroutine 通知多个在等待的 goroutines)的场景。
这个时候,就需要有个全局的变量来标志第一个协程数据是否接受完毕,剩下的协程,反复简单该变量的值,直到满足要求。
反复简单 应该改为 反复检查?
done 不是并发安全的
@2lingnil fixed @Imfan done 前后有锁
除了Signal这个,使用场景和WaitGroup非常相似啊,而且Signal可以用chan自己实现,什么特定场景用Cond更好吗?
这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
writer 函数里面的c.L.Lock() and c.L.Unlock()没啥作用把? 不能说明什么问题
使用示例中,wait既然是阻塞,第五行为什么是用for而不是if呢?
使用示例中,“互斥锁需要保护的条件变量”,条件变量中的互斥锁并不是用来保护 done 的吧,而是用来互斥加入条件变量的等待队列的吧
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
大佬,确实如此。有想到 sync.Cond 的存在的必要性了吗?
@ngyhd 使用示例中,wait既然是阻塞,第五行为什么是用for而不是if呢?
文中有写 "对条件的检查,使用了 for !condition() 而非 if,是因为当前协程被唤醒时,条件不一定符合要求,需要再次 Wait 等待下次被唤醒。为了保险起见,使用 for 能够确保条件符合要求后,再执行后续的代码。"
@whyming 除了Signal这个,使用场景和WaitGroup非常相似啊,而且Signal可以用chan自己实现,什么特定场景用Cond更好吗?
WaitGroup处理多通知一,Cond处理一通知多(且确保并发安全)
@kirklonglive writer 函数里面的c.L.Lock() and c.L.Unlock()没啥作用把? 不能说明什么问题
c.L.Lock()是为了Wait函数内部并发安全的将当前协程加入Cond的通知队列,之后会解锁并挂起等待通知。 c.L.Unlock()并不是配对先前你所见的那个c.L.Lock(),而是配对的wait()内部的加锁。wait()获得通知后,并不是立即就退出函数了,wait内部接着是去抢占c.L锁的,以确保对共享资源的并发安全访问。只有当前协程抢占到c.L锁后才会从wait()退出。之后处理你的业务逻辑,最后需要你显式的c.L.Unlock()去解锁wait()退出前的锁定
@ngyhd 使用示例中,wait既然是阻塞,第五行为什么是用for而不是if呢?
因为存在可能,某个wait协程会把资源状态改回不可用,所以其他wait协程需要二次确认。如果所有wait协程都是只读不写,我也觉着用if就好了
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
Cond不仅仅是广播通知,它要做2个工作: 1 共享资源ready后,广播发出通知 2 收到通知的多个wait协程要处理并发安全的访问共享资源,可参考wait()函数内部实现,获得通知后并不是立即退出wait函数,而是去抢占c.L锁,抢到锁后wait()函数才会返回
@dablelv
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
大佬,确实如此。有想到 sync.Cond 的存在的必要性了吗?
chan不能做广播,waitgroup的Done是负数时会触发panic,而cond任何时候都可以signal/broadcast。我遇到的场景就是非cond不可,你可以把它理解为触发器、心跳之类的。它是阻塞控制而不是并发安全控制
@lysShub
@dablelv
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
大佬,确实如此。有想到 sync.Cond 的存在的必要性了吗?
chan不能做广播,waitgroup的Done是负数时会触发panic,而cond任何时候都可以signal/broadcast。我遇到的场景就是非cond不可,你可以把它理解为触发器、心跳之类的。它是阻塞控制而不是并发安全控制
close channel 不就可以广播了么
那只能广播一次
---原始邮件--- 发件人: @.> 发送时间: 2022年5月8日(周日) 晚上9:25 收件人: @.>; 抄送: @.@.>; 主题: Re: [geektutu/blog] Go sync.Cond | Go 语言高性能编程 | 极客兔兔 (#125)
@lysShub
@dablelv
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
大佬,确实如此。有想到 sync.Cond 的存在的必要性了吗?
chan不能做广播,waitgroup的Done是负数时会触发panic,而cond任何时候都可以signal/broadcast。我遇到的场景就是非cond不可,你可以把它理解为触发器、心跳之类的。它是阻塞控制而不是并发安全控制
close channel 不就可以广播了么
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
@fufay 这个例子用单个channel做“广播”也可以,只要 close(ch)就代表发送通知了,其他的goroutine使用for select 结构来接收广播就行了。
我想到了一点 close channel 实现通知不如条件变量的情形。条件变量通知后可以重复使用,但是 closed 的 channel 却不能。所以还是用条件变量会更好一点。
那么广播后,其余的协程还是要竞争锁吗?
@qq717019142 那么广播后,其余的协程还是要竞争锁吗?
从上面各位大佬的回复来看,需要竞争锁 Wait()后,首先【释放锁】,然后进入【等待通知】的状态,收到【通知】后,开始【竞争锁】,【持有锁】后执行业务逻辑代码,最后【释放锁】
广播时,如果没有其他协程正在wait这个cond,那么这个广播相当于就被丢弃了
---原始邮件--- 发件人: @.> 发送时间: 2022年7月26日(周二) 下午4:46 收件人: @.>; 抄送: @.@.>; 主题: Re: [geektutu/blog] Go sync.Cond | Go 语言高性能编程 | 极客兔兔 (#125)
@qq717019142 那么广播后,其余的协程还是要竞争锁吗?
从上面各位大佬的回复来看,需要竞争锁 Wait()后,首先【释放锁】,然后进入【等待通知】的状态,收到【通知】后,开始【竞争锁】,【持有锁】后执行业务逻辑代码,最后【释放锁】
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
对,BroadCast
和Signal
都只是唤醒正在Wait
的协程,如果没有,那么就相当于不起作用
@lysShub 广播时,如果没有其他协程正在wait这个cond,那么这个广播相当于就被丢弃了
---原始邮件--- 发件人: @.> 发送时间: 2022年7月26日(周二) 下午4:46 收件人: @.>; 抄送: @.@.>; 主题: Re: [geektutu/blog] Go sync.Cond | Go 语言高性能编程 | 极客兔兔 (#125)
@qq717019142 那么广播后,其余的协程还是要竞争锁吗?
从上面各位大佬的回复来看,需要竞争锁 Wait()后,首先【释放锁】,然后进入【等待通知】的状态,收到【通知】后,开始【竞争锁】,【持有锁】后执行业务逻辑代码,最后【释放锁】
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
文中最后的使用实例中的write
应该要添加如下代码,以免第二次调用write
的时候,读者正在读
func write(name string, c *sync.Cond) {
// 添加这几行代码将done修改为false
c.L.Lock()
done = false
c.L.Unlock()
log.Println(name, "starts writing")
time.Sleep(time.Second)
c.L.Lock()
done = true
c.L.Unlock()
log.Println(name, "wakes all")
c.Broadcast()
}