Goroutine 是 Go 程序中最基本的并发执行单元。每一个 Go 程序都至少包含一个 goroutine——main goroutine,当 Go 程序启动时它会自动创建。
在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能——goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个 goroutine 去执行这个函数就可以了,就是这么简单粗暴。
在Go中开启一个协程非常简单,只需在一个普通函数前面加上一个go
关键字,就可以开启一个goroutine
// 执行一个函数
func()
// 开启一个协程执行这个函数
go func()
一个Go
程序的入口通常是main
函数,程序启动后,main
函数最先运行,我们称之为main goroutine
。
在main
中或者其下调用的代码中才可以使用go + func()
的方法来启动协程。
main
的地位相当于主线程,当main
函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。(主线程结束,代表整个程序执行结束,所有资源都会回收。)
package main
import "fmt"
func mytest() {
fmt.Println("hello, go")
}
func main() {
// 启动一个协程
go mytest()
fmt.Println("hello, world")
}
这段代码运行完,只会输出hello, world
,而不会输出hello, go
(因为协程的创建需要时间,当hello, world打印后,协程还没来得及创建并执行)
可以通过time.Sleep
来阻塞主线程,来实现协程的创建并执行。
package main
import (
"fmt"
"time"
)
func main() {
go goRoutine("协程1号")
go goRoutine("协程2号")
time.Sleep(5 * time.Second)
}
func goRoutine(message string) {
for i := 0; i < 10; i++ {
fmt.Printf("%v: %v\n", i, message)
}
time.Sleep(10 * time.Millisecond)
}
执行结果:
$ go run main.go
0: 协程2号
1: 协程2号
2: 协程2号
3: 协程2号
4: 协程2号
5: 协程2号
6: 协程2号
7: 协程2号
8: 协程2号
9: 协程2号
0: 协程1号
1: 协程1号
2: 协程1号
3: 协程1号
4: 协程1号
5: 协程1号
6: 协程1号
7: 协程1号
8: 协程1号
9: 协程1号
当主线程运行完后,所有goroutine都会停止。但在实际开发中,我们无法预知什么时候所有goroutine都会执行完毕。可以通过sync
包的WaitGroup
来实现让主程序等待所有goroutine执行行完再结束。
// var 实例名 sync.WaitGroup
var wg sync.WaitGroup
实例化完成后,就可以使用它的几个方法:
package main
import (
"fmt"
"sync"
)
func worker(x int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Printf("worker %d: %d\n", x, i)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go worker(1, &wg)
go worker(2, &wg)
wg.Wait()
}
基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建