go中CSP模型提倡“不要以共享内存的方式来通信,相反,要以通信来共享内存”,go的CSP模型,就是通过goroutine和channel结合的形式来实现的。
这里通过goroutine和channel实现了一个简单的并发控制,即通过channel在不同的goroutine之间传递信息,而不是通过锁的形式,或者是共享变量的形式来实现。
例如要实现3个goroutine交替输出1-30的数字,可以参考下面的实现方法:
下方会有解释
func getWorker(waitCh chan int, symbol int, wg *sync.WaitGroup) (next chan int) {
notify := make(chan int)
wg.Add(1)
go func(waitCh chan int) {
defer func() {
wg.Done()
}()
for d := range waitCh {
if d >= 30 {
break
}
fmt.Println("goroutine:", symbol, "print", d+1)
notify <- d + 1
}
close(notify)
fmt.Println("goroutine: finish", symbol)
}(waitCh)
return notify
}
func main() {
wg := new(sync.WaitGroup)
start := make(chan int)
lastCh := start
for i := 0; i < 3; i++ {
lastCh = getWorker(lastCh, i+1, wg)
}
start<-0
for v := range lastCh {
start<-v
}
close(start)
wg.Wait()
}
- 每次创建goroutine时,waitCh是该协程等待信号的channel,当这个goroutine完成任务时,发信号到notify channel中。
- waitGroup的目的是为了主协程等待所有goroutine都结束后再结束,即优雅的停止。
- goroutine执行完任务后,将notify channel关闭。
这样,我们在main函数中先创建一个start channel,然后循环创建goroutine,每次创建的goroutine的waitCh为上次创建goroutine返回的notify channel,循环结束后,我们在main函数中将最后一个channel的内容发到start channel中,这样,就构成了一个环。
最后,我们往start channel中发一个信号,goroutine环中就会交替打印数字啦,等到结束条件满足后,某个notifyCh被close,也会使得整个goroutine环都循环被关闭,最终优雅的结束。
下方是3个goroutine交替打印1-10的运行结果: