文章目录
目录
前言
前面的编程文章主要介绍了并发编程的一些原理和应用,主要解决了并发编程中的临界资源保护问题、多线程之间的通信问题、多线程执行顺序问题。但是在实际编程中,也会遇到一类常见问题就是多线程之间的条件执行。多线程之间的条件执行讲的是多个线程出于等待状态,接收到对应的信号后执行。举一个生活化的场景:很多人排队等公交车,每个人都做好了上车的准备,但是车还没有来。只能等,盼星星,盼月亮,公交车来了。公交车来了以后,对于排队候车的人来讲,会有两种情况:1、成功上车2、因为人太多,车辆拒载,没有排上座位。在这个例子当中,公交车上就是那个等待条件,并且发出接纳多少人上车的指令。
一、sync.Cond是什么
sync.Cond位于sync.package的包中,主要功能是提供一个临时变量。通过这个临时变量实现了等待条件不满足时,线程阻塞、等待条件满足后,线程再执行的功能。
二、sync.Cond的使用
1. Sync.Cond初始化
Cond初始化的过程中,需要传入一个Mutex类型的参数。
package main
import "sync"
func main() {
mutex := sync.Mutex{}
cond := sync.NewCond(&mutex)
}
2.Sync.Cond简单使用
下文实现一个简单的Cond使用,实现先开启一个go线程等待,线程阻塞等待至cond.BroadCast执行,实现对等待线程的唤醒。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
signal := 1
mutex := sync.Mutex{}
cond := sync.NewCond(&mutex)
go func() {
cond.L.Lock()
for signal == 1 {
fmt.Println("线程开始等待")
cond.Wait()
fmt.Println("线程等待结束")
}
cond.L.Unlock()
}()
time.Sleep(200*time.Millisecond)
cond.Broadcast()
signal=0
time.Sleep(200*time.Millisecond)
}
3.Sync.Cond的BroadCast方法使用
下文实现一个BroadCast应用,实现10个线程同时阻塞等待,条件变量执行BroadCase后,所有线程并发执行。因为使用的是BroadCast的方法,唤醒所有等待线程,所以使用waitGroup管理批量线程执行。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
signal := 1
mutex := sync.Mutex{}
cond := sync.NewCond(&mutex)
group := sync.WaitGroup{}
group.Add(10)
for i := 0; i < 10; i++ {
go func(int1 int) {
defer group.Done()
cond.L.Lock()
for signal == 1 {
fmt.Printf("线程%d开始等待\n",int1)
cond.Wait()
fmt.Printf("线程%d结束\n",int1)
}
cond.L.Unlock()
}(i)
}
time.Sleep(200*time.Millisecond)
cond.Broadcast()
signal=0
group.Wait()
}
对应的执行效果:
线程4开始等待
线程9开始等待
线程5开始等待
线程6开始等待
线程7开始等待
线程8开始等待
线程1开始等待
线程0开始等待
线程2开始等待
线程3开始等待
线程3结束
线程2结束
线程7结束
线程4结束
线程9结束
线程5结束
线程6结束
线程1结束
线程0结束
线程8结束Process finished with the exit code 0
4.Sync.Cond的Signal()方法使用
下文实现一个BroadCast应用,实现10个线程同时阻塞等待,条件变量执行Signal后,所有线程并发抢占。因为使用的是Signal的方法,只会有一个线程会被执行。这个选择规则就是先到先得谁第一个等待,谁就第一个执行。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
signal := 1
mutex := sync.Mutex{}
cond := sync.NewCond(&mutex)
c := make(chan struct{}, 1)
for i := 0; i < 10; i++ {
go func(int1 int) {
defer func() {
c<- struct{}{}
}()
cond.L.Lock()
for signal == 1 {
fmt.Printf("线程%d开始等待\n",int1)
cond.Wait()
fmt.Printf("线程%d结束\n",int1)
}
cond.L.Unlock()
}(i)
}
time.Sleep(200*time.Millisecond)
cond.Signal()
signal=0
<-c
}
对应的执行效果:
线程0开始等待
线程9开始等待
线程2开始等待
线程3开始等待
线程4开始等待
线程5开始等待
线程6开始等待
线程7开始等待
线程8开始等待
线程1开始等待
线程0结束
三、Cond的内部原理
核心源码如下:
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify) //添加线程到等到等待队列
c.L.Unlock()
runtime_notifyListWait(&c.notify, t) //让等待队列的线程开始等待
c.L.Lock()
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了cond的使用,Cond提供了一种可以县实现多线程协同执行的机制。这种机制应用到了很多的工程中,后续会陆续更新一些相关的内容。
欢迎各位大佬多提宝贵意见,多多留言互动~