Hi,大家好,我是明哥。
在自己学习 Golang 的这段时间里,我写了详细的学习笔记放在我的个人微信公众号《Go 编程时光》,对于 Go 语言,我也算是个初学者,因此写的东西应该会比较适合刚接触的同学,如果你也是刚学习 Go 语言,不防关注一下,一起学习,一起成长。
我的在线博客:http://golang.iswbm.com
我的 Github:github.com/iswbm/GolangCodingTime
说到 Go 语言,很多没接触过它的人,对它的第一印象,一定是它从语言层面天生支持并发,非常方便,让开发者能快速写出高性能且易于理解的程序。
在 Python(为 Py 为例,主要是我比较熟悉,其他主流编程语言也类似)中,并发编程的门槛并不低,你要学习多进程,多线程,还要掌握各种支持并发的库 asyncio,aiohttp 等,同时你还要清楚它们之间的区别及优缺点,懂得在不同的场景选择不同的并发模式。
而 Golang 作为一门现代化的编程语言,它不需要你直面这些复杂的问题。在 Golang 里,你不需要学习如何创建进程池 / 线程池,也不需要知道什么情况下使用多线程,什么时候使用多进程。因为你没得选,也不需要选,它原生提供的 goroutine(也即协程)已经足够优秀,能够自动帮你处理好所有的事情,而你要做的只是执行它,就这么简单。
一个 goroutine 本身就是一个函数,当你直接调用时,它就是一个普通函数,如果你在调用前加一个关键字 go
,那你就开启了一个 goroutine。
// 执行一个函数
func()
// 开启一个协程执行这个函数
go func()
1. 协程的初步使用
一个 Go 程序的入口通常是 main 函数, 程序启动后,main 函数最先运行,我们称之为 main goroutine
。
在 main 中或者其下调用的代码中才可以使用 go + func()
的方法来启动协程。
main 的地位相当于主线程,当 main 函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,也得乖乖退出。
因此如下这段代码运行完,只会输出 hello, world
,而不会输出 hello, go
(因为协程的创建需要时间,当 hello, world
打印后,协程还没来得及并执行)
import "fmt"
func mytest() {fmt.Println("hello, go")
}
func main() {
// 启动一个协程
go mytest()
fmt.Println("hello, world")
}
对于刚学习 Go 的协程同学来说,可以使用 time.Sleep 来使 main 阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(后续我们会学习其他更优雅的方式)。
当我在代码中加入一行 time.Sleep 输出就符合预期了。
import (
"fmt"
"time"
)
func mytest() {fmt.Println("hello, go")
}
func main() {go mytest()
fmt.Println("hello, world")
time.Sleep(time.Second)
}
输出如下
hello, world
hello, go
2. 多个协程的效果
为了让你看到并发的效果,这里举个最简单的例子
import (
"fmt"
"time"
)
func mygo(name string) {
for i := 0; i < 10; i++ {fmt.Printf("In goroutine %s\n", name)
// 为了避免第一个协程执行过快,观察不到并发的效果,加个休眠
time.Sleep(10 * time.Millisecond)
}
}
func main() {go mygo("协程 1 号") // 第一个协程
go mygo("协程 2 号") // 第二个协程
time.Sleep(time.Second)
}
输出如下,可以观察到两个协程就如两个线程一样,并发执行
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
In goroutine 协程 1 号
In goroutine 协程 2 号
通过以上简单的例子,是不是折服于 Go 的这种强大的并发特性,将同步代码转为异步代码,真的只要一个关键字就可以了,也不需要使用其他库,简单方便。
本篇只介绍了协程的简单使用,真正的并发程序还是要结合 信道(channel)来实现。关于信道的内容,将在下一篇文章中介绍。
系列导读
01. 开发环境的搭建(Goland & VS Code)
02. 学习五种变量创建的方法
03. 详解数据类型:整形与浮点型
04. 详解数据类型:byte、rune 与 string
05. 详解数据类型:数组与切片
06. 详解数据类型:字典与布尔类型
07. 详解数据类型:指针
08. 面向对象编程:结构体与继承
09. 一篇文章理解 Go 里的函数
10. Go 语言流程控制:if-else 条件语句
11. Go 语言流程控制:switch-case 选择语句
12. Go 语言流程控制:for 循环语句
13. Go 语言流程控制:goto 无条件跳转
14. Go 语言流程控制:defer 延迟调用
15. 面向对象编程:接口与多态
16. 关键字:make 和 new 的区别?
17. 一篇文章理解 Go 里的语句块与作用域
18. 学习 Go 协程:goroutine
19. 学习 Go 协程:详解信道 / 通道
20. 几个信道死锁经典错误案例详解
21. 学习 Go 协程:WaitGroup
22. 学习 Go 协程:互斥锁和读写锁
23. Go 里的异常处理:panic 和 recover
24. 超详细解读 Go Modules 前世今生及入门使用
25. Go 语言中关于包导入必学的 8 个知识点
26. 如何开源自己写的模块给别人用?
27. 说说 Go 语言中的类型断言?
28. 这五点带你理解 Go 语言的 select 用法