介绍Golang 协程实现并行应用
Go是一种令人难以置信的高性能语言。它拥有大量的优秀特性,可以让你构建出速度惊人的应用程序。通过提供这些goroutine(协程)和channel(通道)机制使得构建并行程序相当简单。
使用协程可以非常快地把顺序应用程序转成并行,无需担心创建线程或线程池。但与所有并发编程一样,这也会带来一些不可控因素,因此在所有函数调用前使用go关键字之前,必须考虑到这些不确定性。本文带你了解如何使用Go协程,提升程序性能。
1. 协程
协程是由Go运行时管理的轻量级线程,让我们能够轻松创建异步并行程序,异步程序执行速度通常比顺序程序要快得多。
协程比线程小得多,它们通常需要2kB的堆栈空间来初始化,而线程需要1Mb的堆栈空间。
Goroutines基于非常少的OS线程实现多路复用,这意味着并发go程序较相同的性能水平Java应用需要更少的资源。创建上千个goroutines通常最多需要一到两个OS线程,而如果在java中做同样的事情,则需要1000个完整的线程,每个线程占用至少1Mb的堆空间。
协程是编译器级的,进程和线程是操作系统级的。协程不被操作系统内核管理,而完全由程序控制,因此没有线程切换的开销。通过将大量个goroutines映射到一个线程上,我们不必担心在应用程序中创建和销毁线程时的性能影响。和多线程比,线程数量越多,协程的性能优势就越明显。协程的最大优势在于其轻量级,可以轻松创建上万个而不会导致系统资源衰竭。
2. 示例
为了演示我们首先创建顺序应用,后面再修改为异步程序。
2.1 顺序执行
定义函数简单根据参数输出多次信息至控制台。
package main
import (
"fmt"
"time"
)
// 定义功能简单的函数,后面进行异步执行
func compute(value int) {
for i := 0; i < value; i++ {
time.Sleep(time.Second)
fmt.Println(i)
}
}
func main() {
fmt.Println("Goroutine Tutorial")
// 顺序执行
compute(10)
compute(10)
// 等待控制台输入阻止程序结束
var input string
fmt.Scanln(&input)
}
如果执行上述代码,可以看到在控制台上打印0~9两次,整个执行时间超过20秒。 fmt.Scanln() 阻止main程序结束,等待两个函数顺序执行完毕。
2.2 异步执行
如果我们不担心程序输出值0到n的顺序,那么我们可以通过使用goroutines并使其异步来加快程序的速度。
package main
import (
"fmt"
"time"
)
// 和前面程序相比没有改变任何内容
func compute(value int) {
for i := 0; i < value; i++ {
time.Sleep(time.Second)
fmt.Println(i)
}
}
func main() {
fmt.Println("Goroutine Tutorial")
// 仅仅再函数签名增加了 go 关键字
go compute(10)
go compute(10)
var input string
fmt.Scanln(&input)
}
和前面程序相比仅仅增加go关键字,但go帮我们创建了两个独立的协程并行执行程序。尝试执行程序,会看到 0,0,1,1,2,2…直到…9,9。如果你计算这个程序的执行时间那么我们就会注意到一下子降到大约10秒。
这里要说明下,如果注释main中最后两行,执行程序不会再控制台上打印任何内容。这是因为main函数在异步函数执行之前已经结束,那么所有的协程也被迫立刻终止,因此我们增加fmt.Scanln()阻止main函数提前结束。
2.3 匿名协程函数
前面示例使用go 调用命名函数,起始也可以并行执行匿名函数:
package main
import "fmt"
func main() {
// 使用 `go` 创建匿名函数
go func() {
fmt.Println("Executing my Concurrent anonymous function")
}()
// 再次组织main提前结束
fmt.Scanln()
}
执行该程序能看到异步匿名函数成功执行并打印结果:
Executing my Concurrent anonymous function
3. 总结
本文学习了Go中开发并行程序,了解协程概念并使用协程加速程序执行、创建高性能应用。
本文参考链接:https://blog.csdn.net/neweastsun/article/details/106969049