(1)用协程交替打印 ABC 一百次;
(2)用协程交替打印数字,0 到 100。
我们可以利用 Channel 的阻塞机制来进行信号传递,因为无缓冲的 Channel 中,如果没有数据传入管道,那么该协程就会被阻塞直到有数据被输入进管道。
问题一:打印 ABC
设计这样一个函数:参数一接收要打印的字母,参数二为 Channel 接收上一个协程传递的信息,参数三也为 Channel 负责给下一个协程传递发送信息,参数四为 sync.WaitGroup 确保线程能够执行完。
此外,这里需要注意的是,我们每个协程完成打印任务的时候就把 Channel 关闭,因此循环到打印 C 的线程时,ChanA 已经被关闭了,此时仍向它发送数据就会报错,所以要通过 recover 方法将异常捕获。
package main
import (
"fmt"
"sync"
)
func printABC(letter string, ch chan struct{}, nxtch chan struct{}, wg *sync.WaitGroup) {
defer func() {
wg.Done() // 宣布协程完成任务
close(ch) // 关闭 channel
if r := recover(); r != nil { //捕获异常
fmt.Printf("\n")
}
}()
for i := 0; i < 3; i++ {
<-ch // 接收信号并消费
fmt.Print(letter)
nxtch <- struct{}{} // 发送信号给下个协程
}
}
func main() {
var wg sync.WaitGroup
chA := make(chan struct{})
chB := make(chan struct{})
chC := make(chan struct{})
wg.Add(3)
go printABC("A", chA, chB, &wg)
go printABC("B", chB, chC, &wg)
go printABC("C", chC, chA, &wg)
// START
chA <- struct{}{}
wg.Wait()
}
问题二:打印 0 到 100
函数的设计逻辑与问题一没有什么区别,主要就是在循环要打印的内容上做了一些更改,根据第一次初始化协程传入的参数开始循环打印。
package main
import (
"fmt"
"sync")
func printNum(start int, ch chan struct{}, nxtch chan struct{}, wg *sync.WaitGroup) {
defer func() {
wg.Done() // 宣布协程完成任务
close(ch) // 关闭 channel
if r := recover(); r != nil { //捕获异常
fmt.Printf("\n")
}
}()
for i := start; i <= 2; i += 2 {
<-ch // 接收信号并消费
fmt.Printf("%d ", i)
nxtch <- struct{}{} // 发送信号给下个协程
}
}
func main() {
var wg sync.WaitGroup
chA := make(chan struct{})
chB := make(chan struct{})
wg.Add(2)
go printNum(0, chA, chB, &wg)
go printNum(1, chB, chA, &wg)
// START
chA <- struct{}{}
wg.Wait()
}