这是我参与「第五届青训营 」伴学笔记创作活动的第1天。
语言进阶
1. Goroutine 协程
线程是属于内核态,创建、切换、停止都属于比较重的系统操作,栈KB级别。
协程可以理解成用户级、轻量级的线程,调度由Go语言本身去完成,栈MB级别。
func hello(i int){
println("hello goroutine:"+fmt.Sprint(i))
}
// 快速打印hello goroutine:0~4
func HelloGoRoutine(){
for i := 0; i<5; i++{
// 调用函数时在函数前加上 go 关键字 便为函数创建一个协程来运行
go func(j int){
hello(j)
}(i) // 匿名函数传入参数
}
// 保证子协程在完成之前主协程不退出
time.Sleep(time.Second)
}
2. CSP(Communicating Sequential Processes)
Go提倡使用通信共享内存(左),而不是使用共享内存来实现通信(右)。使用共享内存来通信的话不可避免的要对临界区加锁,从而影响性能。
3. 通道 Channel
Channel的创建:
make(chan 元素类型, [缓冲大小])
make(chan int) // 无缓冲通道
make(chan int,2) // 有缓冲通道
使用无缓冲通道通信时,会导致发送方goroutine和接收方goroutine同步化,也被称为同步通道。解决同步问题的方式即使用有缓冲通道。
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
// 子协程发送0~9数字
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
// 子协程计算输入数字的平方
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
// 主协程输出最后的平方数
for i := range dest {
println(i)
}
}
4. 并发安全 Lock
对变量执行2000次+1操作,5个协程并发执行。
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 Add(){
x = 0
for i:=0; i<5; i++{
go addWithoutLock()
}
time.Sleep(time.Second)
println("WithoutLock:", x) // WithoutLock: 8382 不加锁输出未知结果
x = 0
for i:=0; i<5; i++{
go addWithLock()
}
time.Sleep(time.Second)
println("WithLock:", x) // WithLock: 10000
}
5. WaitGroup
开启协程+1,执行结束-1,主协程阻塞直到计数器为0。