后端go语言课程笔记|青训营笔记

69 阅读3分钟

#后端go语言课程笔记|青训营笔记

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天,通过学习记录并总结部分知识点,不足之处请指正。 本章知识点有以下方面:

并发VS并行

什么是并发,什么是并行?

并发是不同的代码块交替执行,也就是交替可以做不同的事情。 并行是不同的代码块同时执行,也就是同时可以做不同的事情。

屏幕截图 2023-01-16 230104.png

 package main
  
 import (
     "fmt"
     "runtime"
     "sync"
 )
  
 func main() {
     runtime.GOMAXPROCS(1)
     app := sync.WaitGroup{}
     app.Add(20)
     for i := 0; i < 10; i++ {
         go func() {
             fmt.Println("go ---> 1 i: ", i)
             app.Done()
         }()
     }
     for i := 0; i < 10; i++ {
         go func(i int) {
             fmt.Println("go ---> 2 i: ", i)
             app.Done()
         }(i)
  
     }
     app.Wait()

注意:并发不等于并行

go中的所有的goroutines都是只在一个线程里面跑的。即,以上两个代码不是并行,确是并发的。

屏幕截图 2023-01-16 231151.png

信道

信道(Channel)可以被认为是协程之间通信的管道。与水流从管道的一端流向另一端一样,数据可以从信道的一端发送并在另一端接收。

信道使用前都需要声明,每个信道都有一个与之关联的类型。此类型是允许信道传输的数据类型,除此类型外不能通过信道传输其他类型。

屏幕截图 2023-01-16 231407.png 提倡通过信道共享内存而不是通过共享内存而实现通信

并发安全 lock

如果没有锁,在我们的项目中,可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生数据竞态。

 var x int64
 var wg  sync.WaitGroup
 func add()  {
     for i :=0;i<1000;i++{
         x = x + 1
     }
     wg.Done()
 }
 func main()  {
     wg.Add(2)
     go add()
     go add()
     wg.Wait()
     fmt.Println(x)
 }

如上,这两个goroutine在访问和修改x变量的时候就会出现数据竞争,最后的结果与预期结果不一致。

sync.WaitGroup

Go语言中可以使用sync.WaitGroup来实现并发任务的同步。sync.WaitGroup有以下几个方法:

方法名功能
(wg * WaitGroup) Add(delta int)计数器 + delta
(wg *WaitGroup) Done()计数器-1
(wg *WaitGroup) Wait()阻塞直到计数器变为0

sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少

channel

Channel是go中的一个核心类型,类似于一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。

创建通道是引用类型,通道类型的空值是nil。声明的通道需要使用make函数初始化之后才能使用。创建channel的格式如下:

 make (chan 元素类型,[缓冲大小])

其中,channel的缓冲大小是可选的。

channel操作有:发送(send)、接收(receive)和关闭(close)三种操作。其中,发送和接收都使用<-符号。关闭是调用内置的close函数来关闭通道。

注意:

  • 向一个nil channel发送消息数据,则会一直阻塞;
  • channel关闭后不可以继续向channel发送消息,但可以继续从channel接收消息;
  • 通道是可以被垃圾回收机制回收的,关闭通道不是必须的。