这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记 测试使用
Go语言入门-工程实践
语言进阶(此笔记为作者根据老师讲述内容,会不断补充)
01.并发VS执行
并发:多线程在一个核上cpu运行,我们可以根据示意图看得出来同一个cpu处理俩个事务的时候是
同一核心不同时间的并行:多线程程序在多个核的cpu上运行,特点:
不同核心相同时间
1.1 Goroutine
概念:线程、协程
为什么go更适合高并发场景?
- 线程:内核态 轻量级协程 栈MB级别
- 协程:用户态 一个线程内部多个协程 栈KB级别
其创建核调度由Go语言负责(可以达上万个)
例子:快速打印多个不同的字符串
-
快速:开多个协程去打印
代码:
func hello(i int) {
println("hello :" + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
//i应该是输入的传参
}(i)
}
/*暴力阻塞 保证协程执行完 主线程不退出*/
time.Sleep(time.Second)
}
func main() {
HelloGoRoutine()
}
结果:
hello :3 hello :2 hello :4 hello :0 hello :1
- 分析:我们可以通过结果的乱序输出来得出这个是一个并行的go语言输出
1.2 CSP
通过通讯共享内存
示意图
--------------------------》Gorountine2
Gorountine 》通道channel
--------------------------》Gorountine3
通过共享内存来实现通信
示意图
-------------- 《 Gorountine2
Gorountine 》临界区
--------------《 Gorountine3
1.3 Channel
make(chan 元素类型,[缓冲大小])
- 无缓冲通道 make(chan int)
同步通道:Gorountinne同步化
-
有缓冲通道 make(chan int,2)
生产销配模型:--oo--> (o指代缓冲点<货架> 如果货架内部有
货物占满后需要等前面的被拿走后,下一个货物才能进入货架)
例子:
描述:模仿生产和消费速度不匹配中通过channel 来解决这里面的问题
func CaSquare() {
src := make(chan int)
//有缓冲的通道 解决A线程生产过快 而消费者消费较慢 影响生产者的执行效率
dest := make(chan int, 3)
//A子协程
go func() {
defer close(src)
//生产i 并发送到 src
for i := 0; i < 10; i++ {
src <- i
}
}()
//B子协程
go func() {
//延迟的自由关闭 defer
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
//复杂操作
println(i)
}
}
func main() {
CaSquare()
}
1.4 并发安全 Lock (Sync包下)
对变量进行2000次加锁操作,五个协程并发执行
枷锁
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)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("WithLock:", x)
}
func ManyGoWait() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
结果
测试到第三遍终于出错了
WithoutLock: 9200
WithLock: 10000
通过以上结果,我们可以发现,没加锁的way如果在并发的情况下很有可能会出现错误的结果,因此我们在并发情况下要使用Lock来做并发安全的读写操作
枷锁:对临界区的一个控制 保证并发安全
1.5 WaitGroup(Sync包下)
描述:无法确定协程的结束时间,因此无法精确的设置sleep的时间(按老师的来说就是优雅的阻塞0-0)
方法:
- add(dalta int)计数器+delta
- Done()计数器-1
- Wait()阻塞直到计数器为0
例子:
func ManyGoWait() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello(j)
}(i)
}
wg.Wait()
}
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
标题:第三届字节跳动青训营同学必用工具 - 掘金