总述
Go依靠独特的轻量级的协程 轻松创建上百万个任务不会导致系统的资源衰竭 (相比起来进程和线程最多也不会超过1w个)
接下来主要从这几个方面展开:
- 什么是并发编程
- Go如何实现并发
- 并发的原理
- 协程之间通信依靠的chanel
- 同步异步
- 数据安全
并发
看起来同时
并行
真正同时
📢注意:
- 并行一定是多核的
- 并行看上去很牛逼 但是因为其任务之间的通信成本很高 导致其效果不一定好
进程(process)
每一个独立进行的程序就是进程,是程序中的一次动态执行过程,可与理解为正在执行的程序,是系统资源调度和分配的基本单位,拥有独立的内存单元,需要经历从代码加载,执行,到最后执行完毕。多进程的系统可以同时运行多个程序,因为CPU有自己的分时机制,所以每个程序都可以获取到自己的时间片,但是进程之间的信息交互比较麻烦,另外进程的创建,撤销,切换的开销大。
线程(thread)
线程是一个基本的CPU单元,是CPU调度和分配的最小单位。同一个进程下的线程可以共享系统资源,线程之间的切换代价小于进程之间的切换
协程(goroutine)
是一种用户态的轻量级线程,协程的调度完全通过用户来控制,协程和子函数很类似,一个入口 一次返回 一旦退出 就是完成。
Goroutine
创建的时候 最开始在堆栈上分配一个内存 最开始是4kb 如果不够了再增加 所以go的协程可以同时并发成千上万个goroutine。
我们使用goroutine 的时候是针对某一特定的任务,也可以说是某一段特定的函数or方法,如果一个goroutine里面封装的是main函数,那么我们就称他是主goroutine,
如何使用goroutine
直接在要执行的函数前面加一个go关键字,然后执行即可(这种函数往往没有返回值,就算有也会被舍弃).下面这个例子就是一个goroutine打印数字,另一个goroutine打印字母。
package main
import "fmt"
func main() {
//step1. 先创建一个子goroutinez执行printNum
go printNum()
//step2. main中打印字母
for i := 1; i <= 1000; i++ {
fmt.Printf("\t主goroutine 打印字母A&%d\n ", i)
}
fmt.Println("main func end")
}
func printNum() {
for i := 1; i <= 1000; i++ {
fmt.Printf("子goroutine打印数字: %d\n", i)
}
}
部分运行结果截图如下:
正常情况下,一旦主goroutine执行完毕,程序立即结束,无论子goroutine是否结束,如果想要在所有子goroiutine都结束之后再执行主goroutine就需要用到后面的协程之间通信chanel.
另外,主goroutine还有一点值得注意的是,它会设定每一个goroutine占据空间的最大尺寸, 64位计算机是1GB,32位250M。 如果某个goroutine的栈空间溢出,就会产生一个panic, 随后这个go程序就会终止。 接下来 主goroutine就会进行一系列初始化工作。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。