Go语言进阶|青训营笔记

81 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

并发和并行

并行需要两个或两个以上的线程跑在不同的处理器上,并发可以跑在一个处理器上通过时间片进行切换。可以简单的理解为:

并发:多线程程序在一个核的cpu上运行

并行:多线程程序在多个核的cpu上运行

image-20220508231555374

Goroutine

协程:用户态,轻量级线程,栈MB级别。

线程:内核态,线程跑多个协程,栈KB级别。

协程的出现可以让go语言很轻松的执行并发程序,且开销很小。

image-20220508231807276

channel管道通信

协程之间通信有两种方式:通过通信共享内存通过共享内存实现通信

而go语言实现的是通过通信来共享内存。

image-20220508231941584

无缓冲的channel

image-20220508232034869

  • 在第1步,两个goroutine 都到达通道,但哪个都没有开始执行发送或者接收。
  • 在第2步,左侧的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个goroutine 会在通道中被锁住,直到交换完成。
  • 在第3步,右侧的goroutine 将它的手放入通道,这模拟了从通道里接收数据。这个goroutine一样也会在通道中被锁住,直到交换完成。
  • 在第4步和第5步,进行交换,并最终,在第6步,两个 goroutine 都将它们的手从通道里拿出来,这模拟了被锁住的 goroutine 得到释放。两个 goroutine 现在都可以去做其他事情了。

有缓冲的channel

image-20220508232122265

  • 在第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) 把计数器设置为nDone() 每次把计数器-1wait() 会阻塞代码的运行,直到计数器地值减为0