6.1 go协程

go协程类似一个线程,但是go协程是由go自己调度,而不是系统。在协程中的代码可以和其他代码并发执行。让我们看一个例子:

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func main() {
  7. fmt.Println("start")
  8. go process()
  9. time.Sleep(time.Millisecond * 10) // this is bad, don't do this!
  10. fmt.Println("done")
  11. }
  12. func process() {
  13. fmt.Println("processing")
  14. }

这个例子有一些有趣的事,但是最重要的是了解我们是如何启动一个go协程。我们只是简单的将go关键字附在我们想要执行的函数前面即可。如果我们只想执行一小段代码,例如上面的例子一样,我们可以使用一个匿名函数。需要注意的是,匿名函数不只是在go协程中使用,其他地方也可以。

  1. go func() {
  2. fmt.Println("processing")
  3. }()

go协程很容易创建且开销较小。最终多个go协程将会在同一个底层的系统线程上运行。这也常称之为M:N线程模型,因为我们有M个应用线程(go协程)运行在N个系统线程上。结果就是,一个go协程的开销和系统线程比起来相对很低(一般都是几KB)。在现代的硬件上,有可能拥有成千上万个go协程。

另外,这里还隐藏了映射和调度的复杂性。我们只需要说这段代码需要并发执行,然后让go自己去处理。

如果我们回到刚刚的例子中,你将会注意到我们使用了Sleep让程序等待了几毫秒。这是因为主进程在退出前协程才有机会去执行(主进程在退出前不会等待所有协程都执行完毕)。为了解决这个问题,我们必须让代码协同。

链接