目录一、条件变量与互斥锁二、条件变量与互斥锁的配合使用三、条件变量的使用创建锁和条件使用四、条件变量的Wait方法做了什么一、条件变量与互斥锁 条件变量是基于互斥锁的,它必须基于互斥
条件变量的初始化离不开互斥锁,并且它的方法有点也是基于互斥锁的。
条件变量提供的三个方法:等待通知(wait)、单发通知(signal)、广发通知(broadcast)。
// mailbox 代表信箱
// 0 代表信箱是空的,1代表信箱是满的
var mailbox uint8
// lock 代表信箱上的锁
var lock sync.RWMutex
// sendCond 代表专用于发信的条件变量
var sendCond = sync.NewCond(&lock)
// reveCond 代表专用于收信的条件变量
var reveCond = sync.NewCond(lock.RLocker())
sync.Cond
类型并不是开箱即用的,只能利用sync.NewCond
创建它的指针值。这个函数需要sync.Locker
类型的参数值。sync.Locker
是一个接口,它包含两个指针方法,即Lock()
和Unlock()
;因此,sync.Mutex
和sync.RWMutex
这两个类型的指针类型才是sync.Locker
接口的实现类型。lock.RLocker()
得到的值,拥有Lock和Unlock方法,其内部会分别调用lock变量的RLock方法和RUnlock方法;lock.Lock()
for mailbox == 1 {
sendCond.Wait()
}
mailbox = 1
lock.Unlock()
recvCond.Signal()
lock.RLock()
for mailbox == 0 {
recvCond.Wait()
}
mailbox = 0
lock.RUnlock()
sendCond.Signal()
完整代码:
package main
import (
"log"
"sync"
"time"
)
func main() {
// mailbox 代表信箱
// 0 代表信箱是空的,1代表信箱是满的
var mailbox uint8
// lock 代表信箱上的锁
var lock sync.RWMutex
// sendCond 代表专用于发信的条件变量
var sendCond = sync.NewCond(&lock)
// reveCond 代表专用于收信的条件变量
var reveCond = sync.NewCond(lock.RLocker())
// sign 用于传递演示完成的信号
sign := make(chan struct{}, 2)
max := 5
go func(max int) { // 用于发信
defer func() {
sign <- struct{}{}
}()
for i := 1; i <= max; i++ {
time.Sleep(time.Millisecond * 5)
lock.Lock()
for mailbox == 1 {
sendCond.Wait()
}
log.Printf("sender [%d]: the mailbox is empty.", i)
mailbox = 1
log.Printf("sender [%d]: the letter has been sent.", i)
lock.Unlock()
reveCond.Signal()
}
}(max)
go func(max int) { // 用于收信
defer func() {
sign <- struct{}{}
}()
for j := 1; j <= max; j++ {
time.Sleep(time.Millisecond * 500)
lock.RLock()
for mailbox == 0 {
reveCond.Wait()
}
log.Printf("receiver [%d]: the mailbox is full.", j)
mailbox = 0
log.Printf("receiver [%d]: the letter has been received.", j)
lock.RUnlock()
sendCond.Signal()
}
}(max)
<-sign
<-sign
}
(1)条件变量Wait方法主要做的四件事
条件变量的Wait方法主要做了四件事:
(2)为什么要先要锁定条件变量基于的互斥锁,才能调用它的wait方法
因为条件变量的wait方法在阻塞当前的goroutine之前,会解锁它基于的互斥锁。所以在调用wait方法之前,必须先锁定这个互斥锁,否则在调用这个wait方法时,就会引发一个不可恢复的panic。
如果条件变量的Wait方法不先解锁互斥锁的话,那就会造成两个后果:不是当前的程序因panic而崩溃,就是相关的goroutine全面阻塞。
(3)为什么用for语句来包裹调用的wait方法表达式,用if语句不行吗
if语句只会对共享资源的状态检查一次,而for语句却可以做多次检查,直到这个状态改变为止。
之所以做多次检查,主要是为了保险起见。如果一个goroutine因收到通知而被唤醒,但却发现共享资源的状态,依然不符合它的要求i,那么就应该再次调用条件变量的Wait方法,并继续等待下次通知的到来。
这种情况是很有可能发生的,具体如下面所示:
综上所述,在包裹条件变量的Wait方法的时候,我们总是应该使用for语句。
不要用if语句,因为它不能重复地执行“检查状态 - 等待通知 - 被唤醒”的这个流程。
(4)条件变量的Signal方法和Broadcast方法
条件变量signal方法和Broadcast方法都是用来发送通知的,不同的是,前者的通知只会唤醒一个因此而等待的goroutine,而后者的通知却会唤醒所有为此等待的goroutine。
条件变量的Wait方法总会把当前的 goroutine 添加到通知队列的队尾,而它的Signal方法总会从通知队列的队首开始,查找可被唤醒的 goroutine。所以,因Signal方法的通知,而被唤醒的 goroutine 一般都是最早等待的那一个。
条件变量Signal方法和Broadcast方法放置的位置:
与Wait方法不同,条件变量的Signal方法和Broadcast方法并不需要在互斥锁的保护下执行。恰恰相反,我们最好在解锁条件变量基于的那个互斥锁之后,再去调用它的这两个方法。这更有利于程序的运行效率。
条件变量的通知具有即时性:
如果发送通知的时候没有 goroutine 为此等待,那么该通知就会被直接丢弃。在这之后才开始等待的 goroutine 只可能被后面的通知唤醒。
到此这篇关于golang并发编程中条件变量sync.Cond的使用的文章就介绍到这了,更多相关Go sync.Cond内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: GoLang并发编程中条件变量sync.Cond的使用
本文链接: https://www.lsjlt.com/news/177233.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
2024-04-05
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0