985 words
5 minutes
Go语言教程:使用条件变量实现信箱模型
在本教程中,我们将通过一个简单的信箱模型来学习Go语言中的条件变量(Condition Variable)和互斥锁(Mutex)。这个模型模拟了一个信箱的发送和接收过程,信箱可以是空的或满的。我们将详细解释代码的每一部分,并举一反三,帮助你更好地理解这些概念。
代码概述
以下是我们将要分析的Go代码:
package main
import ( "log" "sync" "time")
func main() { // mailbox 代表信箱。 // 0代表信箱是空的,1代表信箱是满的。 var mailbox uint8 // lock 代表信箱上的锁。 var lock sync.RWMutex // sendCond 代表专用于发信的条件变量。 sendCond := sync.NewCond(&lock) // recvCond 代表专用于收信的条件变量。 recvCond := sync.NewCond(lock.RLocker())
// sign 用于传递演示完成的信号。 sign := make(chan struct{}, 3) max := 5 go func(max int) { // 用于发信。 defer func() { sign <- struct{}{} }() for i := 1; i <= max; i++ { time.Sleep(time.Millisecond * 500) 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() recvCond.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 { recvCond.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. 变量声明
var mailbox uint8var lock sync.RWMutexsendCond := sync.NewCond(&lock)recvCond := sync.NewCond(lock.RLocker())sign := make(chan struct{}, 3)max := 5mailbox:表示信箱的状态,0表示空,1表示满。lock:读写锁,用于保护对mailbox的访问。sendCond:用于发送信的条件变量。recvCond:用于接收信的条件变量。sign:用于传递演示完成的信号。max:表示发送和接收的最大次数。
2. 发信协程
go func(max int) { defer func() { sign <- struct{}{} }() for i := 1; i <= max; i++ { time.Sleep(time.Millisecond * 500) 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() recvCond.Signal() }}(max)- 使用
lock.Lock()加锁,确保对mailbox的访问是线程安全的。 - 如果
mailbox是满的(mailbox == 1),则调用sendCond.Wait()等待。 - 当信箱为空时,发送信件(将
mailbox设为1),并解锁。 - 使用
recvCond.Signal()通知接收协程信箱已满。
3. 收信协程
go func(max int) { defer func() { sign <- struct{}{} }() for j := 1; j <= max; j++ { time.Sleep(time.Millisecond * 500) lock.RLock() for mailbox == 0 { recvCond.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)- 使用
lock.RLock()加锁,确保对mailbox的访问是线程安全的。 - 如果
mailbox是空的(mailbox == 0),则调用recvCond.Wait()等待。 - 当信箱满时,接收信件(将
mailbox设为0),并解锁。 - 使用
sendCond.Signal()通知发送协程信箱已空。
4. 等待协程完成
<-sign<-sign- 等待两个协程完成。
举一反三
1. 多个发送者和接收者
可以扩展代码,使其支持多个发送者和接收者。只需启动多个发送和接收协程,并适当调整条件变量的使用。
2. 使用通道(Channel)
Go语言中的通道(Channel)可以简化这种生产者-消费者模型。以下是使用通道实现的示例:
package main
import ( "log" "time")
func main() { mailbox := make(chan struct{}, 1) 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 * 500) mailbox <- struct{}{} log.Printf("sender [%d]: the letter has been sent.", i) } }(max)
go func(max int) { defer func() { sign <- struct{}{} }() for j := 1; j <= max; j++ { time.Sleep(time.Millisecond * 500) <-mailbox log.Printf("receiver [%d]: the letter has been received.", j) } }(max)
<-sign <-sign}总结
通过这个教程,我们学习了如何使用Go语言中的条件变量和互斥锁来实现一个简单的信箱模型。我们还展示了如何使用通道来简化这种生产者-消费者模型。希望这个教程能帮助你更好地理解Go语言中的并发编程。
Share
If this article helped you, please share it with others!
Go语言教程:使用条件变量实现信箱模型
https://blog.ithuo.net/posts/go-concurrency-mailbox-model-tutorial/ Last updated on 2022-05-26,1324 days ago
Some content may be outdated