这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
并发与并行
对于GO语言来说,充分发挥了多核优势,实现了高效运行
并发
并发指的是多线程程序在一个核的CPU上运行,通过时间片的切换来实现多个任务同时运行的状态。
并行
并行指的是多线程程序在多个核的CPU上运行
协程与线程
协程
用户态,轻量级现成,栈MB级别
线程
内核态,线程跑多个协程,栈KB级别
Goroutine
快速打印hello goroutine:0~4
func hello(i int) {
println("hello goroutine : "+ fmt.Sprint(i))
}
func HelloGoRoutine() {
for i:= 0;i<5;i+ {
go func(j int) {
hello(j)
}(i)
time.Sleep(time.Second)
这段代码能够实现乱序输出
调用函数时加上go为函数调用协程运行函数
CSP(Communicating Sequential Processes)
go语言提倡通过通信来共享内存,而并非通过共享内存来实现通信 通过channel来实现不同协程之间数据的通信
Channel
创建方式: make(chan 元素类型,[缓冲大小]) 分为有缓冲通道和无缓冲通道 。
无缓冲通道
使用无缓冲通道时,创建channel对象时无需声明缓冲大小
使用无缓冲通道会导致收发的协程处于同步状态,所以也被称为同步通道。
有缓冲通道
生产-消费模型
当缓冲区中数据存放满后会产生阻塞,无法继续向其中继续发送数据。
例程
package concurrence
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
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)
}
}
例程效果
A子协程发送发数字0~9
B子协程计算输入数字的平方
主协程输出最后的平方数
实现思路
由于生产者的生产速度大于消费者的消费速度,此时使用带缓冲的通道
A协程生成0~9后输出到同步通道src中
B协程从src中取出数字,计算平方数后输出到异步通道dest中。
最后主协程遍历dest,并将其输出
并发安全 Lock
对变量执行2000次+1操作,5个协程并发执行。
在加锁的情况下,变量输出为10000,符合预期值
在不加锁的情况下,变量输出为随机值,这种不加锁的行为被称为undefine
WaitGroup
等待协程同步,可以使用waitgroup,而并非sleep。
开启协程时使用Add(n)
关闭协程使用Done()
等待计数器为0使用Wait()