条件变量
简单描述:条件变量是线程中的东西,就是等待某一条件的发生,和信号一样。
详细描述:条件变量是线程的另外一种同步机制,这些同步对象为线程提供了会合的场所,理解起来就是两个(或者多个)线程需要碰头(或者说进行交互-一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则接收条件已经发生改变的信号。
关于条件变量,如果没有真正遇到使用场景,可能很难理解,我这里简述一个使用情况:当我们想做5件事,a件事是主要的,其他的是次要的,分别是b,c,d,f,但是呢b,c,d,f四件事可以并行处理,然后我们就通过四个协程来处理,但是b,c,d,f这四个事情又需要等待a事情做完func1之后才能做,这个时候条件变量就在这个应用中很关键,下面我们来看一个例子。
package main
import (
"sync"
"fmt"
"time"
)
var l = new(sync.Mutex)
var cond = sync.NewCond(l)
func main() {
//下面我们讲cord的使用
//Cond实现了一个条件变量,用于等待或宣布事件发生时goroutine的交汇点。
for i := 0; i < 5; i++ {
go test(i)
}
fmt.Println("start all")
time.Sleep(time.Second * 2)
fmt.Println("broadcast")
// 下发一个通知给已经获取锁的goroutine,这里是主线程做完某些事情之后在子线程等待的那个时候告诉他可以继续了。
cond.Signal()
time.Sleep(time.Second * 2)
//3秒之后 下发广播给所有等待的goroutine,这里是主线程做完某些事情之后在剩下的子线程等待的那个时候告诉他可以继续了。
cond.Broadcast()
time.Sleep(time.Second * 15)
time.Sleep(time.Second * 15)
fmt.Println("finish all")
}
func test(x int) {
cond.L.Lock() //获取锁
fmt.Println("aaa: ", x)
cond.Wait() //等待通知 暂时阻塞
fmt.Println("bbb: ", x)
time.Sleep(time.Second * 1)
cond.L.Unlock() //释放锁
}
sync.Once的使用,sync.Once确保了即使在不同的goroutine上,调用Do传入的函数只执行一次,这里就是保持一个变量只能执行一次,在很多实际应用中都可能会用到。
package main
import (
"sync"
"fmt"
)
var l = new(sync.Mutex)
func main() {
//初始化c
c := 0
//增加一个全局锁
lock := new(sync.Mutex)
incr := func() {
lock.Lock() // 1
defer lock.Unlock() // 2
c++
fmt.Printf("incr: %d\n", c)
}
// o 是sync.Once 只能执行一次,我们看一下结果,o值的是1
o := new(sync.Once)
w := new(sync.WaitGroup)
for i := 0; i <= 100; i++ {
w.Add(1)
go func() {
defer w.Done()
o.Do(incr)
}()
}
w.Wait()
}
返回结果
incr: 1
好了,以上sync包的重点都讲完了,从sync.Pool,sync.WaitGroup,sync.RWMutex,sync.Mutex,sync.NewCond,sync.Once都是我们使用go中很常用的。如果想学好go,这几个的使用,一定要牢牢掌握。