这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
课堂笔记
本堂课重点内容
GO语言快的原因
GO可以发挥多核优势高效运行
详细知识点介绍
并发
- 多线程程序在一个核的CPU上运行
并行
- 多线程程序在多个核的CPU上运行
Gorotine
适合高并发是因为GO语言创建多个协程
协程
- 用户态
- 轻量级线程
- 栈MB级别
线程
- 内核态
- 线程跑多个协程
- 栈KB级别
只需要在调用函数的时候在函数前加上一个go的关键词即可
CSP(Communicating Sequential Processes)
通过通信来实现共享内存,而不是通过内存来实现共享通信
Channel(引用类型)<通信来实现共享内存>
make(chan 元素类型,[缓冲大小])
无缓冲通道 make(chan int)
- 接受和发送的Gorotine和发送的Gorotine同步化<同步通道>
有缓冲通道 make(chan int ,2)
- 接受和发送的Gorotine和发送的Gorotine异步化<异步通道>
使用缓冲通道保证顺序性->保证并发安全
- 消费者的逻辑复杂->消费速度慢
- 生产者的逻辑简单->生产速度快
并发安全Lock<内存实现通信>
多种Gorotine同时操作一块内存资源->数据并发
- 通过对权限的控制来保证并发安全
WaitGroup
无法精确知道协程进行的时间
- 通过设置一个精确的sleep的时间——WaitGroup包
| 函数 | 功能 |
|---|---|
| Add(data int) | 计数器+data |
| Done() | 计数器-1 |
| Wait() | 阻塞直到计数器为0 |
- 执行所有子协程之前使用Add函数记录数目
- 每执行完一个子协程就使用Done完成任务
- 执行完子协程的函数在Wait处等待其他子协程执行结束
实践练习例子
举例——快速打印Hello goroutine
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGORoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
举例——A子协程发送0-9,B子协程计算输入数字的平方,主协程输出最后的平方数
使用缓冲通道保证顺序性->保证并发安全
此时A子协程就是生产者->快速生产
B子协程就是消费者->慢速消费
package main
func main() {
src := make(chan int)
dest := make(chan int, 3)
//A
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
//B
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
//M
for i := range dest {
println(i)
}
}
举例——对变量执行2000+1次操作,5个协程并发进行
通过结果可以发现没有Lock的打印结果并非10000,并且无规律
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func main() {
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
println("WithoutLock : ", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("withLock : ", x)
}
课后个人总结
- go语言的快是建立在协程的基础上
- 需要好好掌握协程相关的函数来充分发挥go语言快的性能