这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
并发和并行
并行需要两个或两个以上的线程跑在不同的处理器上,并发可以跑在一个处理器上通过时间片进行切换。可以简单的理解为:
并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行
Goroutine
协程:用户态,轻量级线程,栈MB级别。
线程:内核态,线程跑多个协程,栈KB级别。
协程的出现可以让go语言很轻松的执行并发程序,且开销很小。
channel管道通信
协程之间通信有两种方式:通过通信共享内存、通过共享内存实现通信
而go语言实现的是通过通信来共享内存。
无缓冲的channel
- 在第1步,两个goroutine 都到达通道,但哪个都没有开始执行发送或者接收。
- 在第2步,左侧的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个goroutine 会在通道中被锁住,直到交换完成。
- 在第3步,右侧的goroutine 将它的手放入通道,这模拟了从通道里接收数据。这个goroutine一样也会在通道中被锁住,直到交换完成。
- 在第4步和第5步,进行交换,并最终,在第6步,两个 goroutine 都将它们的手从通道里拿出来,这模拟了被锁住的 goroutine 得到释放。两个 goroutine 现在都可以去做其他事情了。
有缓冲的channel
- 在第1步,右侧的 goroutine正在从通道接收一个值。
- 在第2步,右侧的这个goroutine独立完成了接收值的动作,而左侧的goroutine 正在发送—个新值到通道里。
- 在第3步,左侧的goroutine还在向通道发送新值,而右侧的 goroutine 正在从通道接收另外一个值。这个步骤里的两个操作既不是同步的,也不会互相阻塞。
- 最后,在第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。
使用channel可以实现生产者和消费者
写一个、读一个保证并发安全
使用带缓冲的通道解决消费者速度问题
package main
func main() {
// 通过管道实现消费者和生产者
create := make(chan int)
consumer := make(chan int, 3)
// 开启协程
go func() {
defer close(create)
for i := 1; i <= 5; i++ {
create <- i
}
}()
// 开启协程
go func() {
defer close(consumer)
for ch := range create {
consumer <- ch * ch
}
}()
for i := range consumer {
println(i)
}
}
sync包下的关键字
并发安全LOCK与Unlock()
waitGroup
并发任务的同步,使用wait进行阻塞
WaitGroup能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成。
WaitGroup对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait()用来控制计数器的数量。Add(n)把计数器设置为n,Done()每次把计数器-1,wait()会阻塞代码的运行,直到计数器地值减为0。