这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
Day3
Go 语言进阶
本节课将从工程实践角度,讲授在企业项目实际开发过程中的所遇的难题,重点讲解 Go 语言的进阶之路,以及在其依赖管理管理过程中如何演进。
课程资料
1.锁Lock
语言进阶(并发编程)
并发:多线程程序在一个核的CPU上运行。
并行:多线程程序在多个核的CPU上运行。
Go可以充分利用多核。
协程:用户态,轻量级线程,栈KB级别。
线程:内核态,线程跑多个协程,栈MB级别。
Goroutine
package main
import (
"fmt"
"time"
)
func hello(i int){
fmt.Println("hello goroutine:", i)
}
func main(){
for i:=0; i<5; i++ {
// 要将i作为参数传进去,否则会全打印5。
go func(j int){
hello(j)
}(i)
}
time.Sleep(time.Second)
}
CSP
通信顺序进程(Communicating Sequential Processes):
提倡通过通信(通道)共享内存而不是通过共享内存(临界区)实现通信。
Channel
make(chan EleType, [bufferSize])
不指定bufferSize时为无缓冲通道。
无缓冲通道 vs 1缓冲通道
bufferSize 为 0 意味着存进去一个元素后会堵塞。
bufferSize 为 1 存进去一个元素后不会堵塞。
无缓冲通道:必须在接受方与发送方同时准备好时,通道才能正常传递数据,否则双方只有一方在线都会阻塞。
有缓冲通道:当缓冲区满时,发送数据会阻塞,当缓冲区空时,接受数据会阻塞。发送方与接收方不需要同时做好准备。
package main
import "fmt"
func main(){
// 无缓冲
src := make(chan int)
// 1缓冲
dst := make(chan int, 1)
// 生产
go func(){
// 谁生产谁关闭。写完 chan 之后一定要关闭close。
defer close(src)
for i:=0; i<10; i++ {
src <- i
}
}()
// 加工
go func(){
// 可尝试不关闭
defer close(dst)
for i := range src {
dst <- i*i
}
}()
// 消费
// 在遍历时,如果 dst 没有关闭,则会出现 deadlock 的错误。
// 在遍历时,如果 dst 已经关闭,则会正常遍历完数据然后退出。
for i := range dst {
fmt.Println(i)
}
// 继续读有缓冲chan的数据:
// 如果已关闭,得到的是零值(对于int,就是0)。
// 如果没有关闭,fatal error: all goroutines are asleep - deadlock!
fmt.Println(<-dst)
}
并发安全Lock
开启5个协程、每个协程令全局变量x上20000时,加锁和不加锁的结果。
package main
import (
"fmt"
"sync"
"time"
)
// 全局变量
var (
x int64
lock sync.Mutex
)
func addWithLock(){
for i:=0; i<20000; i++ {
// 访问临界区前:加锁
lock.Lock()
// 访问临界区
x++
// 访问临界区后:解锁
lock.Unlock()
}
}
func addWithOutLock(){
for i:=0; i<20000; i++ {
x++
}
}
func main(){
x = 0
for i:=0; i<5; i++ {
go addWithOutLock()
}
time.Sleep(time.Second)
fmt.Println("WithOutLock", x)
x = 0
for i:=0; i<5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("WithLock", x)
}
WithOutLock 67004
WithLock 100000
WaitGroup
Add(delta int):计数器 +delta。
Done():计数器 -1。
Wait():阻塞直到计数器为0。
func main(){
var wg sync.WaitGroup
// 计数器设为 5,因为将开启 5 个协程
wg.Add(5)
for i:=0; i<5; i++ {
go func(j int){
// 本协程结束后,计数器-1
defer wg.Done()
hello(j)
}(i)
}
// 阻塞,直到计数器为 0,即 5 个协程都执行完成
wg.Wait()
}