这是我参与「第五届青训营 」伴学笔记创作活动的第2天。
第一天我们已经接触了go语言的基本语法,今天的主要是记录课程当中的并发编程内容。
一、go语言并发编程特点
广义上的并发是指多线程程序在一个核的cpu上运行,主要是通过切换时间片来实现同时运行,并行是多线程程序直接利用多核来实现运行。而go语言是可以设置使用的核心数,充分发挥多核计算机的优势,提高运行效率。
二、详细知识点
1.goroutine
并发调用函数可以在函数前面加上go关键字,相当于给函数创建一个协程运行。time.sleep(time.Second)可以强制主线程等待上面的进程结束再往下运行。
for i := 0;i<5;i++ {
go hello(i)
}
time.sleep(time.Second)
2.有缓冲通道进行协程通信
这里协程A会生产一个数字提供给src,B协程就会将A协程生产的数据src遍历读取并对其进行平方处理(实现线程和线程的通信)。主线程会对B线程中dest获取的平方数据进行读取。缓冲是指make(chan int, n)中n的参数,如果n为0是不带缓冲。而且dest是有缓冲的,比如dest里面是[0,1,4]时,此时dest已满,src写不进去,当主线程读取0以后,dest就能把下一个src也就是3*3=9写入dest,此时dest就变为[1,4,9]。
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()//协程A
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()//协程B
for i := range dest {
println(i)
}
3.并发安全lock
这里代码是对全局变量x执行2000次+1操作,由5个协程并发进行。由两种方式运行,一种是调用lock进行加锁运行,一种是直接运行,最终加锁的结果x为10000,不加锁的结果x为5312(不加锁结果是随机的)。不加锁的结果比较好理解,主要是多个协程同时调用x+1的操作(可能同时进行的是3个协程,也可能是4个,每个协程调用x+1的时间我们也无法确定),而x能够记录的值只有一个,导致同时进行的x+1操作只有1个是有效的,其他全是无效操作。加锁是对x+1操作时进行控制,不让其他协程进行x+1的操作,这样能够保证5个协程执行的所有x+1操作都是有效的。
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func main(){
x = 0
for i:=0;i<5;i++{
go func(){
for i:=0;i<2000;i++{
lock.Lock()
x+=1
lock.Unlock()
}
}()
}
time.Sleep(time.Second)
println("WithLock:",x)
x = 0
for i:=0;i<5;i++{
go func(){
for i:=0;i<2000;i++{
x+=1
}
}()
}
time.Sleep(time.Second)
println("WithoutLock:",x)
}
4.等待并发任务执行
Sync.WaitGroup是一个计数器功能,能够让主协程阻塞等待所有协程结束。主要操作是:wait阻塞主协程等待协程结束;add能够让计数器增加,这里add(5)是指有5个协程阻塞主协程进行;每个协程执行结束后要进行done操作使协程减一。
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()
总结
本节课主要是学习了一些go语言并发编程的方法。