[关闭]
@phper 2018-03-13T10:09:12.000000Z 字数 2485 阅读 221

21.go协程(Goroutine)

Golang

原文:https://golangbot.com/goroutines/


欢迎来到Golang 系列教程的第 21 章。

在前面的教程里,我们探讨了并发,以及并发与并行的区别。本教程则会介绍在 Go 语言里,如何使用 Go 协程(Goroutine)来实现并发。

Go 协程是什么?

Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。

Go 协程相比于线程的优势

如何启动一个 Go 协程?

调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行。

让我们创建一个 Go 协程吧。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func hello() {
  6. fmt.Println("Hello world goroutine")
  7. }
  8. func main() {
  9. go hello()
  10. fmt.Println("main function")
  11. }

在线运行程序

在第 11 行,go hello() 启动了一个新的 Go 协程。现在 hello() 函数与 main() 函数会并发地执行。主函数会运行在一个特有的 Go 协程上,它称为 Go 主协程(Main Goroutine)。

运行一下程序,你会很惊讶!

该程序只会输出文本 main function。我们启动的 Go 协程究竟出现了什么问题?要理解这一切,我们需要理解两个 Go 协程的主要性质。

现在你应该能够理解,为何我们的 Go 协程没有运行了吧。在第 11 行调用了 go hello() 之后,程序控制没有等待 hello 协程结束,立即返回到了代码下一行,打印 main function。接着由于没有其他可执行的代码,Go 主协程终止,于是 hello 协程就没有机会运行了。

我们现在修复这个问题。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func hello() {
  7. fmt.Println("Hello world goroutine")
  8. }
  9. func main() {
  10. go hello()
  11. time.Sleep(1 * time.Second)
  12. fmt.Println("main function")
  13. }

在线运行程序

在上面程序的第 13 行,我们调用了 time 包里的函数 Sleep,该函数会休眠执行它的 Go 协程。在这里,我们使 Go 主协程休眠了 1 秒。因此在主协程终止之前,调用 go hello() 就有足够的时间来执行了。该程序首先打印 Hello world goroutine,等待 1 秒钟之后,接着打印 main function

在 Go 主协程中使用休眠,以便等待其他协程执行完毕,这种方法只是用于理解 Go 协程如何工作的技巧。信道可用于在其他协程结束执行之前,阻塞 Go 主协程。我们会在下一教程中讨论信道。

启动多个 Go 协程

为了更好地理解 Go 协程,我们再编写一个程序,启动多个 Go 协程。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func numbers() {
  7. for i := 1; i <= 5; i++ {
  8. time.Sleep(250 * time.Millisecond)
  9. fmt.Printf("%d ", i)
  10. }
  11. }
  12. func alphabets() {
  13. for i := 'a'; i <= 'e'; i++ {
  14. time.Sleep(400 * time.Millisecond)
  15. fmt.Printf("%c ", i)
  16. }
  17. }
  18. func main() {
  19. go numbers()
  20. go alphabets()
  21. time.Sleep(3000 * time.Millisecond)
  22. fmt.Println("main terminated")
  23. }

在线运行程序

在上面程序中的第 21 行和第 22 行,启动了两个 Go 协程。现在,这两个协程并发地运行。numbers 协程首先休眠 250 微秒,接着打印 1,然后再次休眠,打印 2,依此类推,一直到打印 5 结束。alphabete 协程同样打印从 ae 的字母,并且每次有 400 微秒的休眠时间。 Go 主协程启动了 numbersalphabete 两个 Go 协程,休眠了 3000 微秒后终止程序。

该程序会输出:

  1. 1 a 2 3 b 4 c 5 d e main terminated

程序的运作如下图所示。为了更好地观看图片,请在新标签页中打开。

image

第一张蓝色的图表示 numbers 协程,第二张褐红色的图表示 alphabets 协程,第三张绿色的图表示 Go 主协程,而最后一张黑色的图把以上三种协程合并了,表明程序是如何运行的。在每个方框顶部,诸如 0 ms250 ms 这样的字符串表示时间(以微秒为单位)。在每个方框的底部,123 等表示输出。蓝色方框表示:250 ms 打印出 1500 ms 打印出 2,依此类推。最后黑色方框的底部的值会是 1 a 2 3 b 4 c 5 d e main terminated,这同样也是整个程序的输出。以上图片非常直观,你可以用它来理解程序是如何运作的。

Go 协程的介绍到此结束。祝你愉快。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注