题目解释
用go实现一个多协程打印数字的功能,要求每个协程之间轮流、依次、按顺序交替打印数字。
举例:2个协程,依次轮流按顺序打印数字10,协程m1打印数字1,协程m2打印数字2,接着协程m1打印数字3,协程m2打印数字4,以此类推
m1:1
m2:2
m1:3
m2:4
m1:5
m2:6
...
给定协程数M,和要打印的数字范围N,保证所有的goroutine是按顺序执行,执行完之后所有协程正常退出。
实现
有序的实现,参考链表的结构,串起来依次执行,所以我定义了一个结构
type ListChan struct {
ch chan int
id string
next *ListChan
wg *sync.WaitGroup
}
完整的实现如下:
import (
"fmt"
"sync"
)
type ListChan struct {
ch chan int
id string
next *ListChan
wg *sync.WaitGroup
}
// Print 用于输出的方法
func (l *ListChan) Print(N int, finish chan struct{}) {
go func() {
defer l.wg.Done()
for {
select {
case num := <-l.ch:
if num > N {
finish <- struct{}{}
return
}
fmt.Printf("%s: %d\n", l.id, num)
l.next.ch <- num + 1
case <-finish:
finish <- struct{}{}
return
}
}
}()
}
// 用于构建和运行多个顺序实例的方式
func Create(M, N int) {
ls := make([]*ListChan, M)
wg := sync.WaitGroup{}
finish := make(chan struct{}, M)
for i := 0; i < M; i++ {
ls[i] = &ListChan{
ch: make(chan int),
id: fmt.Sprintf("m%d", i+1),
wg: &wg,
}
}
for i := 0; i < M; i++ {
if i == M-1 {
ls[i].next = ls[0]
} else {
ls[i].next = ls[i+1]
}
wg.Add(1)
ls[i].Print(N, finish)
}
ls[0].ch <- 1
wg.Wait()
for i := 0; i < M; i++ {
close(ls[i].ch)
}
close(finish)
}
func main() {
Create(2, 10)
}
执行完成后退出的方式不够优雅,采用了一个用于发送停止指令的有缓存chan,本想在数量超过N时,通过close(l.next.ch)来循环关闭,可这种方式非常容易死锁,有没有更优雅的退出方式呀?评论里留下你的答案吧