Goroutine
goroutine是Go并行设计的核心。goroutine说到底其实就是协程,它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
一般情况下,一个普通计算机跑几十个线程就有点负载过大了,但是同样的机器却可以轻松地让成百上千个goroutine进行资源竞争。
Goroutine的创建
只需在函数调⽤语句前添加 go 关键字,就可创建并发执⾏单元。开发⼈员无需了解任何执⾏细节,调度器会自动将其安排到合适的系统线程上执行。
并发编程中,我们通常想将一个过程切分成几块,然后让每个goroutine各自负责一块工作,当一个程序启动时,主函数在一个单独的goroutine中运行,我们叫它main goroutine。新的goroutine会用go语句来创建。而go语言的并发设计,让我们很轻松就可以达成这一目的。
下面以代码示例看看携程的简单用法
package main
import (
"fmt"
"time"
)
func task1(ch chan <- struct{}) {
fmt.Println("任务1耗时3s")
//fmt.Println(time.Now().Format("2006-01-02 15:04:05"))
time.Sleep(time.Second * 3)
//1任务完成
ch <- struct{}{}
}
func task2(ch chan <- struct{}) {
fmt.Println("任务2耗时2s")
time.Sleep(time.Second *2)
//2任务完成
ch <- struct{}{}
}
func main() {
fmt.Println("两个耗时任务开始")
startTime := time.Now()
fmt.Println(startTime.Format("2006-01-02 15:04:05"))
//每次完成任务往ch管道中写空结构体
ch := make(chan struct{})
count := 2 // count 表示总任务的个数
go task1(ch)
//其他任务
go task2(ch)
for range ch{
// 每次从ch中接收数据,表明一个任务的协程结束
count--
// 当所有活动的协程都结束时,关闭管道
if count == 0 {
//总共耗时
finishTime := time.Now()
d := finishTime.Sub(startTime)
fmt.Println("总耗时")
fmt.Println(d)
close(ch)
}
}
}
执行结果:
两个耗时任务开始
2020-08-10 17:19:44
任务2耗时2s
任务1耗时3s
总耗时
3.0117613s
Goroutine特性
主goroutine退出后,其它的工作goroutine也会自动退出:
package main
import (
"fmt"
"time"
)
func newTask() {
i := 0
for {
i++
fmt.Printf("new goroutine: i = %d\n", i)
time.Sleep(1 * time.Second) //延时1s
}
}
func main() {
//创建一个 goroutine,启动另外一个任务
go newTask()
fmt.Println("main goroutine exit")
}
执行结果:
main goroutine exit