go语言条件变量
title: Go语言条件变量
tags: Go语言并发
categories: Go语言标准库
- Go语言条件变量是基于互斥锁的,也就是通过互斥锁实现的。
- 条件变量作用:条件变量并不能保护临界区和共享资源,主要是用来控制访问空闲资源的协程的状态。对阻塞的协程进行等待和唤醒动作。当满足某个条件时,协程才能对共享资源进行操作。
- 简单举例使用
func main() {
var sign int
var lock sync.Mutex
receive:=sync.NewCond(&lock)
send:=sync.NewCond(&lock)
ch := make(chan struct{},3)
//当sign = 1时发送数据,当sign=0时接受数据
go func() {
defer func() {
ch<- struct{}{}
}()
for i := 0; i < 3; i++ {
lock.Lock()
for sign == 0 {
send.Wait()
}
sign = 0
fmt.Println("接受到了数据",i)
lock.Unlock()
receive.Signal()
}
}()
go func() {
defer func() {
ch<- struct{}{}
}()
for j := 4; j < 7; j++ {
lock.Lock()
for sign == 1{
receive.Wait()
}
sign = 1
fmt.Println("发送数据",j)
lock.Unlock()
send.Signal()
}
}()
<-ch
<-ch
}
//运行结果
// 发送数据 4
// 接受到了数据 0
// 发送数据 5
// 接受到了数据 1
// 发送数据 6
// 接受到了数据 2
-
sync.Cond无法直接使用,需要sync.NewCond()函数生成,函数需要一个sync.Locker类型的参数值,sync.Locker是一个接口,有lock()和Unlock()两个方法,需要传入一个实现这两个方法的结构体指针,而互斥锁正好实现了这两个方法,所以从这里可以说条件变量是基于互斥锁的。
-
sync.Cond有三个方法,分别是Wait(),Signal(),Broadcast(),有这三个方法实现对协程的等待通知操作。
-
Wait()方法:
- 将调用它的goroutine(也就是当前的goroutine)加入到当前当前条件变量的等待队列。
- 自动对条件变量的互斥锁进行解锁,所以在使用wait()前要先进行加锁操作。
- 将当前goroutine设置成等待状态,只有条件变量通知(Signal()方法)时才觉得是否唤醒。后面的代码并不会继续执行。
- 当接到通知时会唤醒goroutine,在唤醒后会对条件变量进行加锁,然后继续执行后面的代码,注意会面的代码要对互斥锁进行解锁操作。
- 注意事项:使用for语句可以对条件进行对此检查,使用if的话只能检查一次。主要是为了防止goroutine被唤醒时,条件任然不符合,那它应该继续等待。
-
Signal()方法:
- 通知处于等待状态中的一个goroutine,将其唤醒。Wait()方法会将goroutine放于等待队列的队尾,Signal()方法会从等待队列的队头开始唤醒
- Signal()并不需要在互斥锁的保护下进行,不过在通知时最好先对互斥锁进行解锁操作。
- 条件变量的通知具有即时性。也就是说,如果发送通知的时候没有 goroutine 为此等待,那么该通知就会被直接丢弃。在这之后才开始等待的 goroutine 只可能被后面的通知唤醒。
-
Broadcast()方法:
- 通知等待队列的所有goroutine,将其全部唤醒。