go 进阶 | 青训营笔记

63 阅读2分钟
这是我参与[第五届青训营]笔记创作活动的第一天。

简介

本笔记主要是记录关于go并发编程的四个关键知识,包括 goroutine, channel, lock, WaitGroup。

goroutine

goroutine可以认为是go语言的协程,它运行在一个或多个线程中,是go高并发执行的基础。

使用go关键字来创建一个goroutine。

go func() {
    // func body
}()

channel

channel又名通道,是Go语言中不同goroutine通信的管道,其基本用于共享一块内存。

其基本操作为:

  • 创建
    channel的类型:
  1. 可取可存
    chan T // 可以接收和发送类型为T的数据
  1. 只能发数据
    chan<- T // 只能发送类型为T的数据
  1. 只能收数据
    <-chan T // 只能接收类型为T的数据

通过make以及chan关键字生成一个通道, 当缓冲大小缺失时,为无缓冲通道,否则为对应缓冲大小的通道。

    ch := make(chan 元素类型,[缓冲大小])
    src := make(chan int)
    dest := make(chan int, 3)
  • 遍历
    利用range可以使用遍历通道中的元素
    for i := range src
  • 存取操作 利用<-操作符,当 channel <- data 为发送数据到通道中,当 data <- channel 为从通道中取数。
    channel <- v
    v := <- channel

lock

lock 为 go 语言中的锁,利用其可以实现对于共享内存中的正确访问。假设现在 go 程序中有两个 goroutine 在同时运作,那么他们都需要对于一个相同的元素 x = 0 先取值再递增,此时第一个 goroutine 正在运行,当它完成取数操作时,就进行协程切换,此时第二个 goroutine 运行,它也取到了 x, 那么此时 x 的值为 1, 而不为 2,所以这正是我们对于共享内容中需要上锁的原因。

lock 有上锁操作和取消上锁操作。

以下是一个简单的 go lock 的程序

var (
    x int64
    lock sync.Mutex // 定义互斥锁
)

func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock() // 上锁
        x += 1
        lock.Unlock() // 取消上锁
    }
}

func addWithoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}

func main() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    time.Sleep(time.Second)
    println("WithoutLock", x) // 9137

    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock", x) // 10000
}

WaitGroup

使用 WaitGroup 的原因是 main(或者其它一些函数) 其实是一个主任务,有时需要保证多个并发任务完成时,再继续执行主任务,所以需要先使主任务暂停以等待并发任务的完成。

WaitGroup 一共有三个方法:

  • Add 方法用于设置 WaitGroup 的计数值,可以理解为子任务的数量
  • Done 方法用于将 WaitGroup 的计数值减一,可以理解为完成一个子任务
  • Wait 方法用于阻塞调用者,直到 WaitGroup 的计数值为0,即所有子任务都完成

使用实例

func hello(i int) {
    println("goroutine : ", i)
}

func main() {
   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()
}

以上仅为个人理解,如有错误,烦请指正。