Golang Goroutine的使用
什么是Goroutine
goroutine是Go并行设计的核心。goroutine说到底其实就是协程,它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。
执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
一般情况下,一个普通计算机跑几十个线程就有点负载过大了,但是同样的机器却可以轻松地让成百上千个goroutine进行资源竞争。
Goroutine的创建
只需在函数调⽤语句前添加go关键字,就可创建并发执⾏单元。
开发⼈员无需了解任何执⾏细节,调度器会自动将其安排到合适的系统线程上执行。
在并发编程中,我们通常想将一个过程切分成几块,然后让每个goroutine各自负责一块工作,当一个程序启动时,主函数在一个单独的goroutine中运行,我们叫它maingoroutine。新的goroutine会用go语句来创建。而go语言的并发设计,让我们很轻松就可以达成这一目的。
例如:
packagemain import( "fmt" "time" ) funcfoo(){ i:=0 fortrue{ i++ fmt.Println("newgoroutine:i=",i) time.Sleep(time.Second) } } funcmain(){ //创建一个goroutine,启动另外一个任务 gofoo() i:=0 fortrue{ i++ fmt.Println("maingoroutine:i=",i) time.Sleep(time.Second) } }
结果:
maingoroutine:i= 1
newgoroutine:i= 1
newgoroutine:i= 2
maingoroutine:i= 2
maingoroutine:i= 3
newgoroutine:i= 3
...
Goroutine特性
主go程退出后,其它的子go程也会自动退出:
packagemain import( "fmt" "time" ) funcfoo(){ i:=0 fortrue{ i++ fmt.Println("newgoroutine:i=",i) time.Sleep(time.Second) } } funcmain(){ //创建一个goroutine,启动另外一个任务 gofoo() time.Sleep(time.Second*3) fmt.Println("maingoroutineexit") }
运行结果:
newgoroutine:i= 1
newgoroutine:i= 2
newgoroutine:i= 3
maingoroutineexit
runtime包
Gosched
runtime.Gosched()用于出让当前go程所占用的CPU时间片,让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次再获得cpu时间轮片的时候,从该出让cpu的位置恢复执行。
有点像跑接力赛,A跑了一会碰到代码runtime.Gosched()就把接力棒交给B了,A歇着了,B继续跑。
例如:
packagemain import( "fmt" "runtime" "time" ) funcmain(){ //创建一个goroutine gofunc(sstring){ fori:=0;i<2;i++{ fmt.Println(s) } }("world") fori:=0;i<2;i++{ runtime.Gosched() fmt.Println("hello") } time.Sleep(time.Second*3) }
运行结果:
world
world
hello
hello
如果没有runtime.Gosched()则运行结果如下:
hello
hello
world
world
注意:runtime.Gosched()只是出让一次机会,看下面的代码,注意运行结果:
packagemain import( "fmt" "runtime" "time" ) funcmain(){ //创建一个goroutine gofunc(sstring){ fori:=0;i<2;i++{ fmt.Println(s) time.Sleep(time.Second) } }("world") fori:=0;i<2;i++{ runtime.Gosched() fmt.Println("hello") } }
运行结果:
world
hello
hello
为什么world只有一次呢?因为之前我们说过,主goroutine退出后,其它的工作goroutine也会自动退出。
Goexit
调用runtime.Goexit()将立即终止当前goroutine执⾏,调度器确保所有已注册defer延迟调用被执行。
注意与return的区别,return是返回当前函数调用给调用者。
例如:
packagemain import( "fmt" "runtime" "time" ) funcmain(){ gofunc(){ deferfmt.Println("A.defer") func(){ deferfmt.Println("B.defer") runtime.Goexit()//终止当前goroutine fmt.Println("B")//不会执行 }() fmt.Println("A")//不会执行 }()//不要忘记() time.Sleep(time.Second*3) }
运行结果:
B.defer
A.defer
GOMAXPROCS
调用runtime.GOMAXPROCS()用来设置可以并行计算的CPU核数的最大值,并返回上一次(没有则是电脑默认的)设置的值。
packagemain import( "fmt" "runtime" ) funcmain(){ runtime.GOMAXPROCS(1)//将cpu设置为单核 fortrue{ gofmt.Print(0)//子go程 fmt.Print(1)//主go程 } }
运行结果:
111111...1000000...0111...
在执行runtime.GOMAXPROCS(1)时,最多同时只能有一个goroutine被执行。所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。
packagemain import( "fmt" "runtime" ) funcmain(){ runtime.GOMAXPROCS(2) fortrue{ gofmt.Print(0) fmt.Print(1) } }
运行结果:
111111111111111000000000000000111111111111111110000000000000000011111111100000...
在执行runtime.GOMAXPROCS(2)时,我们使用了两个CPU,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。
runtime包中的其它函数
中文文档在这里:https://studygolang.com/pkgdoc
这里就简单列举一下一些函数以及功能。
funcGOROOT()string
GOROOT返回Go的根目录。如果存在GOROOT环境变量,返回该变量的值;否则,返回创建Go时的根目录。
funcVersion()string
返回Go的版本字符串。它要么是递交的hash和创建时的日期;要么是发行标签如"go1.3"。
funcNumCPU()int
NumCPU返回本地机器的逻辑CPU个数(真·八核)。
funcGC()
GC执行一次垃圾回收。(如果你迫切的希望做一次垃圾回收,可以调用此函数)
到此这篇关于GolangGoroutine的使用的文章就介绍到这了,更多相关GolangGoroutine内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!