Condition variables in Golang difference between Signal and
Golang 标准库中提供了sync.Mutex 用于多线程之间的同步。
实例说明
上面的例子中,通过条件变量唤醒睡眠的线程,有两种方式,Signal和Broadcast,如果是Signal会在因当前条件变量而进入睡眠的线程中随机选取一条线程唤醒,然后该线程试图获取锁,如果获取成功,则执行之后的代码逻辑,如果未获取成功则会一直等待,直到获取锁。如果是Broadcast,会将所有因当前条件变量而进入睡眠的线程全部唤醒,所有的线程一起去试图获取锁,哪一条线程先获取到,哪一条线程先执行,其余的线程则继续等待,直到上一次抢到锁的线程释放锁时,再一次开始对于锁的争抢。(注意这里的线程被条件变量唤醒之后,即使未抢到锁,也不再需要条件变量对其进行再一次的通知唤醒) 下面通过代码后打印结果来更深刻的体会package main
import (
"sync"
"fmt"
"time"
"math/rand"
)
type T struct {
l *sync.Mutex // 锁
c *sync.Cond //条件变量
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
var t *T = new(T)
t.l = new(sync.Mutex)
// 使用条件变量前,必须将其与一个锁绑定
t.c = sync.NewCond(t.l)
//启动10条协程,当协程执行conditions()发现满足条件时,打印出processed,代表该协程处理结束
//当不满足条件时,通过条件变量进入睡眠状态,每次从睡眠状态醒来并且获取锁之后,打印出一条wait。
for i := 0; i < 10; i++ {
go func() {
t.l.Lock()
defer t.l.Unlock()
//不满足条件时通过条件变量进入沉入
//t.c.Wait() 首先会释放与该条件变量绑定的锁,然后在进入睡眠状态
for !conditions() {
t.c.Wait()
fmt.Println("wait")
}
fmt.Println("processed")
}()
}
// 启动一条协程 每隔两秒发送一次通知
go func() {
for {
time.Sleep(time.Second * 2)
//fmt.Println("nnnnBroadcast")
//t.c.Broadcast()
fmt.Println("nnnnSignal")
t.c.Signal()
}
}()
wg.Wait()
}
//生成随机数, 当生成的数为0时,则为满足条件返回true
func conditions() bool {
i := rand.Intn(10)
fmt.Println(i)
if i == 0 {
return true
} else {
return false
}
}
打印结果10条协程,在第一次执行时,两条随机出了0,处理完毕,剩余8条协程,通过条件变量进入睡眠状态。
1
7
7
9
1
8
5
0
processed
6
0
processed
过两秒之后发出第一次广播,其中一条协程获取了锁,并且打印了wait进入conditions()随机出了7,为满足条件继续通过条件变量进入睡眠。
Signal
wait
7
同上
Signal
wait
8
再一次唤醒协程,此次conditions()随机出了0满足了条件,打印出processed,处理完毕。
Signal
wait
0
processed
从上面的打印结果可以看出每一次只唤醒了一条携程 接下来将代码中的 fmt.Println("nSignal")
t.c.Signal()
替换为 fmt.Println("nBroadcast")
t.c.Broadcast()
再来打印输出结果进行分析 第一次打印出的结果和上面的一样主要看发送广播时的输出
1
7
7
9
1
8
5
0
processed
6
0
processed
停了两秒发送了广播,此时所有关联在条件变量的协程都被唤醒。
从结果可以看出,只发送了一次唤醒广播,所有的协程都打印了一遍wait,说明这些协程都被唤醒,并且依次获取了锁,然后执行。当然一个时间段内只能有一个协程获取到锁。
Broadcast
wait
7
wait
8
wait
0
processed
wait
5
wait
1
wait
8
wait
7
wait
1
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |