Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述
让两个goroutine顺序打印数据。
二、思路分析
- 在其他语言中,相当于让两个线程交替打印数据,在go语言中,则是使用协程即goroutine。
- 启动三个channel,分别存放打印数据A令牌,打印数据B令牌,以及任务完成通道,用于主线程等待协程完成任务。
- 通道内存空结构体,因为空结构体不占用内存,
printDog协程,首先从cat通道接收数据,如果cat通道无数据,则printCat协程未完成打印操作,当前printDog协程阻塞。如果cat通道有数据,则PrintDog协程完成打印操作,并向dog 通道放数据,通知printCat协程进行操作。 - 打印1和2与此类似,开启两个通道分别用于通知其协程已完成操作。
main函数中,向cat通道发送数据,通知dog进行打印,最后cat协程完成任务,向done通知任务已完成,main函数从done接收数据,取消阻塞。
三、AC 代码
顺序打印cat,dog
package main
import (
"fmt"
"sync"
)
var dog = make(chan struct{}, 1)
var cat = make(chan struct{}, 1)
var done = make(chan struct{}, 1)
var wg sync.WaitGroup
func printDog() {
wg.Add(1)
defer wg.Done()
defer close(dog)
for i := 0; i < 4; i++ {
<-cat
fmt.Println("dog")
dog <- struct{}{}
}
}
func printCat() {
wg.Add(1)
defer wg.Done()
defer close(cat)
for i := 0; i < 4; i++ {
<-dog
fmt.Println("cat")
cat <- struct{}{}
}
done <- struct{}{}
}
func main() {
cat <- struct{}{}
go printDog()
go printCat()
wg.Wait()
<-done
}
顺序打印1与2
package main
import "fmt"
var one = make(chan struct{}, 1)
var two = make(chan struct{}, 1)
var done = make(chan struct{})
func PrintOne() {
defer close(one)
for i := 0; i < 10; i++ {
//必须由缓冲,不然two被拿走,才可以向下执行
<-two
fmt.Println("1")
one <- struct{}{}
}
}
func PrintTwo() {
defer close(two)
for i := 0; i < 10; i++ {
<-one
fmt.Println("2")
two <- struct{}{}
}
done <- struct{}{}
}
func main() {
defer close(done)
two <- struct{}{}
go PrintOne()
go PrintTwo()
<-done
}
四、总结
顺序打印数据是多线程(多协程)的常见题目,有多种实现方法,go由于通道十分易用,并且推崇使用通信共享内存,而不是共享内存通信。