先上实例代码,后面再来详细讲解。
/** * 通过协程,多任务执行 * 会聊天的Tom和Jerry * 通过 channel 让两个goroutine协作起来 */ package main import ( "fmt" "sync" ) func main() { wg := sync.WaitGroup{} chTom := make(chan string) chJerry := make(chan string) wg.Add(1) // Tom task go func() { for { w := <-chTom if w == "Hello Tom" { fmt.Println("Tom : Yeah, Hello Jerry.") chJerry <- "Hello Jerry" } else { fmt.Println("Tom : Nice day.") chJerry <- "Nice day" wg.Done() } } }() wg.Add(1) // Jerry task go func() { for { w := <-chJerry if w == "Hello Jerry" { fmt.Println("Jerry : Fine, Tom.") chTom <- "Fine Tom" } else { wg.Done() } } }() //fmt.Println("Jerry : Hello Tom.") //chTom <- "Hello Tom" fmt.Println("Tom : Hello Jerry.") chJerry <- "Hello Jerry" wg.Wait() }
这是一个很简单的通过channel实现的go并发编程实例。
一开始定义了两个channel(消息)chTom和chJerry。然后通过Go关键词运行了两个协程(任务),分别代表Tom和Jerry,各自监听来自于channel中的消息,然后做出应答。
实例代码中还引入了一个WaitGroup对象wg,作用是控制主程序的退出时间。因为2个协程是并发运行的,并不会阻塞主程序,如果主程序运行马上结束,也会销毁主程序中所有的协程,这样就没法执行后续的Tom&Jerry互动了。wg.Wait()就是让主程序等待两个协程全部执行完成wg.Done(),然后再结束主程序。
再来看下两个代码的业务逻辑,不论是先给chTom,还是chJerry发送消息,Tom&Jerry都会接收到消息,然后针对不同的消息做出不一样的应答。这里Tom最多有2次应答,Jerry最多有一次应答,他们之间的互动就会结束。所以,整个过程是非常简单明了吧。
为什么Go程序更多建议用channel而不是共享变量来做协程间的数据传递呢?从上面的例子中我们也能看出,channel传递在代码逻辑上更加清晰,使用上也很简单,避免了共享变量的数据安全性问题,不用担心锁的使用。实例中的channel都是string类型,如果使用更加复杂的struct对象,那么传递的数据也可能是 map, [], chan 等,可以完成更多的互动功能。任务间协作这么简单,是不是也想动手试一试了。
欢迎关注实战课程《PHP秒杀系统 高并发高性能的极致挑战》