go语言并发编程|青训营笔记

40 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

go语言并发编程

  • 什么是并发编程?

    并发编程可以使程序不是顺序执行的,而是可以多个分支同时执行,在go中被称为goroutine。
  • 创建goroutine:

    在golang中创建一个goroutine非常简单,仅需要一个go关键字,就可创建一个新的goroutine,每个goroutine都对应函数,可以创建多个goroutine去并发的执行函数。
  • runtime包:

    runtime.Gosched():退出当前gorountine,让其他gorountine先执行,最后再执行被退出的gorountine。 runtime.Goexit():退出当前gorountine,以后也不执行。
  • channel:

    channel可以进行gorountine与gorountine间的通信,内部结构类似于队列遵循先进先出的原则。
  • 并发安全和锁:

    有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。例如两个goroutine去累加变量x的值,这两个goroutine在访问和修改x变量的时候就会存在数据竞争,导致最后的结果与期待的不符。在这种情况下需要加锁以保证并发安全。
    • 互斥锁示例:
      package main
      
      import (
         "fmt"
         "sync"
      )
      
      var x int64
      var wg sync.WaitGroup
      var lock sync.Mutex
      
      func add() {
         for i := 0; i < 5000; i++ {
            lock.Lock() // 加锁
            x ++
            lock.Unlock() // 解锁
         }
         wg.Done()
      }
      // 结果与预期一致
      func main() {
         wg.Add(2)
         go add()
         go add()
         wg.Wait()
         fmt.Println(x)
      }
      
  • Sync:

    为了使协程都能完成工作,前面我们是使用time.Sleep()来生硬的控制执行时间,但这在实际应用中一般是不可取的,为此Go语言提供了 sync.WaitGroup 来实现并发任务的同步。 sync.WaitGroup 内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。 简单使用如下:
    var wg sync.WaitGroup
    
    func hello() {
        defer wg.Done()
        fmt.Println("Hello Goroutine!")
    }
    func main() {
        wg.Add(1)
        go hello() // 启动另外一个goroutine去执行hello函数
        fmt.Println("main goroutine done!")
        wg.Wait()
    }
    
    另:Go语言中内置的map不是并发安全的,Sync包中提供了sync.Map以确保map并发安全。