原文:https://golangbot.com/goroutines/
在上一篇教程中,我们讨论了并发,以及并发和并行的区别。在这篇教程中我们将讨论在Go中如何通过Go协程实现并发。
Go协程(Goroutine)是与其他函数或方法同时运行的函数或方法。可以认为Go协程是轻量级的线程。与创建线程相比,创建Go协程的成本很小。因此在Go中同时运行上千个协程是很常见的。
在函数或方法调用之前加上关键字 go
,这样便开启了一个并发的Go协程。
让我们创建一个协程:
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
在 Playground 中运行
第11行,go hello()
开启了一个新的协程。现在 hello()
函数将和 main()
函数一起运行。main
函数在单独的协程中运行,这个协程称为主协程。
运行这个程序,你将得到一个惊喜。
程序仅输出了一行文本: main function
。我们创建的协程发生了什么?我们需要了解Go协程的两个属性,以了解为什么发生这种情况。
我想你已经知道了为什么我们的协程为什么没有运行。在11行调用 go hello()
后,程序的流程直接调转到下一条语句执行,并没有等待 hello
协程退出,然后打印 main function
。接着主协程结束运行,不会再执行任何代码,因此 hello
协程没有得到运行的机会。
让我们修复这个问题:
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
在 Playground 中运行
上面的程序中,第13行,我们调用 time 包的 Sleep
函数来使调用该函数所在的协程休眠。在这里是让主协程休眠1秒钟。现在调用 go hello()
有了足够的时间得以在主协程退出之前执行。该程序首先打印 Hello world goroutine
,等待1秒钟之后打印 main function
。
在主协程中使用 Sleep 函数等待其他协程结束的方法是不正规的,我们用在这里只是为了说明Go协程是如何工作的。信道可以用于阻塞主协程,直到其他协程执行完毕。我们将在下一篇教程中讨论信道。
让我们写一个程序开启多个协程来更好的理解协程。
package main
import (
"fmt"
"time"
)
func numbers() {
for i := 1; i <= 5; i++ {
time.Sleep(250 * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(400 * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep(3000 * time.Millisecond)
fmt.Println("main terminated")
}
在 Playground 中运行
上面的程序在第21和22行开启了两个协程。现在这两个协程同时执行。numbers
协程最初睡眠 250 毫秒,然后打印 1,接着再次睡眠然后打印2,以此类推,直到打印到 5。类似地,alphabets
协程打印从 a 到 e 的字母,每个字母之间相隔 400 毫秒。主协程开启 numbers
和 alphabets
协程,等待 3000 毫秒,最后终止。
程序的输出为:
1 a 2 3 b 4 c 5 d e main terminated
下面的图片描述了这个程序是如何工作的,请在新的标签中打开图像以获得更好的效果:)
上图中,蓝色的线框表示 numbers
协程,栗色的线框表示 alphabets
协程。绿色的线框表示主协程。黑色的线框合并了上述三个协程,向我们展示了该程序的工作原理。每个框顶部的 0ms,250 ms 的字符串表示以毫秒为单位的时间,在每个框底部的 1,2,3 表示输出。
蓝色的线框告诉我们在 250ms
的时候打印了1,在 500ms
的时候打印了2,以此类推。因此最后一个线框底部的输出:1 a 2 3 b 4 c 5 d e main terminated
也是整个程序的输出。上面的图像是很好理解的,您将能够了解该程序的工作原理。
Go协程的介绍就到这里。祝你有美好的一天!
目录
上一篇:Golang教程:(二十)并发介绍
下一篇:Golang教程:(二十二)信道