这是我参与「第五届青训营 」笔记创作活动的第3天
由于我观看这节课视频的时候总是显示音频出现问题,直接卡住,所以内容较少
主要内容
- 锁 Lock
- 线程同步 WaitGroup
- GO Module
详细介绍
Goroutine
线程:内核态,栈MB级别; 协程:用户态,栈是KB级别的,可以理解为轻量级线程;
一个线程可以启动多个协程,协程的创建和调度由Go语言本身去完成。我们可以一次启动上万的协程,这就是Go更适合高并发原因,当我们需要快速处理某些事情时,就可以启动多个协程并发完成。
在Go语言中,用go关键字开启一个Goroutine
package main
import (
"fmt"
"time"
)
func showMsg(msg string) {
for i := 0; i < 5; i++ {
fmt.Printf("msg: %v\n", msg)
time.Sleep(time.Millisecond*100)
}
}
func main() {
go showMsg("java")
showMsg("golang")
}
在这个程序中,输出的顺序是不定的,而且 java输出的个数可能少于5个。顺序不一定的原因是主协程和其他线程是并发执行的,并没有先后顺序之分;java输出的个数可能少于5个的原因是当主协程执行完成,程序会自动退出,这时其他协程不一定执行完成,为了让所有的java都打印输出,可以在main函数的最后添加一个time.Sleep(time.Second)等待一会,后面将介绍WaitGroup来优雅的实现协程同步。
CSP
Go是通过通信来共享内存,而不是共享内存来实现通信
channel
channel是先进先出的,分为有缓存和无缓存两种
package main
import (
"fmt"
"math/rand"
"time"
)
var values = make(chan int)
func send() {
rand.Seed(time.Now().UnixNano())
value:=rand.Intn(10)
fmt.Printf("send: %v\n", value)
time.Sleep(time.Second)
values<-value
}
func main() {
defer close(values)
go send()
fmt.Println("wait...")
value:=<-values
fmt.Printf("receive: %v\n", value)
fmt.Println("end...")
}
Lock-mutex
package main
import (
"fmt"
"sync"
)
var i int = 100
var lock sync.Mutex
var wg sync.WaitGroup
func add() {
defer wg.Done()
lock.Lock()//加锁后,在解锁前其他资源无法访问i
i += 1
fmt.Printf("i++: %v\n", i)
lock.Unlock()
}
func sub(){
defer wg.Done()
lock.Lock()
i-=1
fmt.Printf("i--: %v\n", i)
lock.Unlock()
}
func main() {
for t := 0; t < 100; t++ {
wg.Add(2)
go sub()
go add()
}
wg.Wait()
fmt.Printf("end>>>>i: %v\n", i)
}
WaitGroup
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func showMsg(i int) {
defer wg.Done()//相当于wg.Add(-1)
fmt.Printf("i: %v\n", i)
}
func main() {
for i:=0;i<10;i++ {
go showMsg(i)
wg.Add(1)
}
wg.Wait()//这里只有在为0的时候才会往下执行,否则会一直等待
fmt.Println("end...")
}
Go Module
以前Go诞生过许多的包管理方式,都比较麻烦,而且对程序文件放置的位置也有限制,而现在使用go mod管理包,只需要在源文件的根目录下执行go mod init package_name即可