Go 语言入门 13—并发
goroutine
goroutine(协程)是 go 语言中独有的一种用于并发编程的机制,在 Java 或 C++中,如果需要实现并发编程,通常需要我们自己维护一个线程池,然后将需要并发的内容包装成一个个的任务放到线程池中去执行,但是在 go 语言中就不一样,goroutine 是由 go 的运行时调度和管理的。go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU 去执行,不需要编程人员操心任务调度等问题。
使用 goroutine
当某个任务需要并发执行时,我们只需要将并发执行的任务包装成一个函数,开启一个 goroutine 去执行该函数即可,开启 goroutine 就是在函数调用前加上go
关键字。
代码示例:
上述代码是没有开启 goroutine 进行并发的代码,所以在代码运行时,testGoroutine
方法和main
方法串行执行,依次打印test goroutine...和test main...
。
接下来在 testGoroutine 函数调用之前加上关键字go
。
运行结果:
在testGoroutine
函数调用前加上go
关键字后反而只打印出了test main...
,这是因为程序在启动时首先会为 main 函数创建一个默认的 goroutine,当 main 函数执行完的时候该 goroutine 也就结束了,所以在 main 函数中启动的 goroutine 也就会随着 main 函数结束而一同结束。
要解决这个只需要在 main 函数中加上time.Sleep
,让 main 函数等一会,给 testGoroutine 充分的时间执行即可。
运行结果:
这样就会先打印出test main...
,然后再打印test goroutine...
,因为通过关键字 go 启动 testGoroutine 函数时,创建 goroutine 会花费一定的时间,但是 main 是在已经创建好的 goroutine 中直接执行的,所以就会先打印出test main...
。
使用 go 语言并发编程就是这么简单,同时还可以启动多个 goroutine。
代码示例:
代码通过 for 循环启动 10 个 goroutine ,分别在每个 goroutine 中都打印传入的变量 i 。
运行结果:
每一次打印的数字的顺序都应该是不一样的,因为一共启动了 10 个 goroutine ,这 10 个 goroutine 是并发执行的,而且 goroutine 是随机调度的。
go 关键字除了使用在调用函数之前,还可以将 go 关键字和函数的声明写到一起。
代码示例:
使用该写法同样可以正常启动 10 个 goroutine 并发,代码正常运行。
runtime 包
runtime 包是 go 语言并发编程中很重要的一个包,这个包里面包含了很多系统资源、并发操作的函数。
系统资源获取
runtime.GOOS:获取操作系统类型
runtime.GOARCH:获取操作系统内核
runtime.NumCPU():获取操作系统核心数
runtime.NumGoroutine():获取当前正在执行和排队的任务总数
代码示例:
运行结果:
runtime.Gosched()
runtime.Gosched()意味着当前 goroutine 让出 CPU,供其它的goroutine
获得执行的机会。同时,当前的goroutine
也会在未来的某个时间点继续运行,假设有一下代码:
在这个代码中,大部分情况下都是会打印出 main ,然后程序就正常结束了,具体原因上面有说到,可以通过 time.Sleep(time.Second) 来控制 main 函数等待子协程执行完毕,同时我们还可以使用 runtime.Gosched() 让 main 主协程让出 CPU,供子协程获得执行的机会。
代码示例:
运行结果:
当程序运行到 runtime.Gosched() 时 main 会让出 CPU,所以就会先打印出两个 test 然后再打印出 main 。
runtime.Goexit()
结束当前goroutine
,其他的goroutine
不受影响,主程序也一样继续运行,但是在结束 goroutine 时,会先执行该 goroutine 中的 defer 语句。
代码示例:
运行结果:
上述代码所示,一共启动了两个 goroutine ,在 goroutine A 中调用的 runtime.Goexit() ,所以 goroutine A 会退出,但是在退出之前会先执行 defer 语句的内容,结束语句之后的打印语句就不会再执行。虽然 goroutine A 退出了,但是 goroutine B 和 main 主协程都不会受到影响。
runtime.GOMAXPROCS
runtime.GOMAXPROCS 指定使用多少个操作系统线程来执行当前的 go 语言程序,在 go1.5 之前默认使用的单核执行,在 go1.5 之后,默认使用的当前系统的 CPU 核心数,例如当前系统是 8 核心,则就会将代码同时调度到 8 个操作系统线程上执行。
使用一个线程:
当把 GOMAXPROCS 设置成 1 时,在代码中同时启动两个 goroutine ,则系统会先将一个任务执行完成之后才会执行第二个任务,所以当前程序打印的结果中,永远都是一个线程从 0 打印到 9,然后再才打印另外一个线程。(无论尝试多少次都是这样)
运行结果:
使用默认线程数( go 版本为 1.17 所以默认为操作系统核心数)
由于没有设置 GOMAXPROCS ,所以使用了默认的线程数,也就是当前系统的核心数,这样就会将代码调度到多个操作系统线程上执行,和上面不一样的是如果尝试执行多次,就会出现线程 1 和线程 2 交替打印的情况。
运行结果:
版权声明: 本文为 InfoQ 作者【良猿】的原创文章。
原文链接:【http://xie.infoq.cn/article/222e1292bab51d2165a9f46f3】。文章转载请联系作者。
评论