Skip to content

并发

并发--协程

  • 启动携程
    • 代码示例
      package main
      
      import (
          "fmt"
          "time"
      )
      
      func loop() {
          for i := 1; i < 11; i++ {
              fmt.Printf("%d,", i)
          }
      }
      
      func main() {
          go loop()
          go loop()
          time.Sleep(time.Second * 5)
      }
      
      
      // 输出
      MacBook-Pro:gorotine elasticnotes$ go run gorotine.go 
      1,2,3,4,5,6,1,2,3,4,5,6,7,8,9,10,7,8,9,10,
      MacBook-Pro:gorotine elasticnotes$ go run gorotine.go 
      1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,9,10,
      
  • 多核CPU设置
    • 代码示例
      package main
      
      import (
          "fmt"
          "runtime"
          "time"
      )
      
      func loop() {
          for i := 1; i < 11; i++ {
              fmt.Printf("%d,", i)
          }
      }
      
      func main() {
      
          fmt.Printf("cpu num is %d", runtime.NumCPU())
      
          // GOMAXPROCS 设置最大核心数   NumCPU 逻辑的cpu个数
          runtime.GOMAXPROCS(runtime.NumCPU() - 1)
          go loop()
          go loop()
          time.Sleep(time.Second * 5)
      }
      

并发--协程通信

  • channel
    • 代码示例
      package main
      
      import (
          "fmt"
          "runtime"
          "time"
      )
      
      // int类型,创建了一个channel,缓存是10
      var chanInt chan int = make(chan int, 10)
      
      // 发送数据到channel
      func Send() {
          time.Sleep(time.Second * 1)
          chanInt <- 1
          time.Sleep(time.Second * 1)
          chanInt <- 2
          time.Sleep(time.Second * 1)
          chanInt <- 3
      }
      
      // 接收channel中的数据
      func Rec() {
          num := <-chanInt
          fmt.Println("num is:", num)
          num = <-chanInt
          fmt.Println("num is:", num)
          num = <-chanInt
          fmt.Println("num is:", num)
      }
      
      func main() {
          runtime.GOMAXPROCS(runtime.NumCPU())
          go Send()
          go Rec()
          time.Sleep(time.Second * 5)
      }
      
      
      // 输出
      MacBook-Pro:channel elasticnotes$ go run channel.go 
      num is: 1
      num is: 2
      num is: 3
      
  • select
    • 代码示例
      package main
      
      import (
          "fmt"
          "runtime"
          "time"
      )
      
      // int类型,创建了一个channel,缓存是10
      var chanInt chan int = make(chan int, 10)
      var timeout chan bool = make(chan bool)
      
      // 发送数据到channel
      func Send() {
          time.Sleep(time.Second * 1)
          chanInt <- 1
          time.Sleep(time.Second * 1)
          chanInt <- 2
          time.Sleep(time.Second * 1)
          chanInt <- 3
          time.Sleep(time.Second * 2)
          timeout <- true
      }
      
      // 接收channel中的数据
      func Rec() {
          for {
              select {
              case num := <-chanInt:
                  fmt.Println("num: ", num)
              case <-timeout:
                  fmt.Println("timeout . . .")
              }
          }
      }
      
      func main() {
          runtime.GOMAXPROCS(runtime.NumCPU())
          go Send()
          go Rec()
          time.Sleep(time.Second * 60)
      }
      
      
      // 输出
      MacBook-Pro:channel elasticnotes$ go run channel.go 
      num:  1
      num:  2
      num:  3
      timeout . . .
      

并发--协程同步

  • 系统工具 sync.waitgroup
    1. Add(delta int) 添加协程记录
    2. Done() 移除协程记录
    3. Wait() 同步等待所有记录的协程全部结束
  • 代码示例
    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
        "time"
    )
    
    // int类型,创建了一个channel,缓存是10
    var chanInt chan int = make(chan int, 10)
    var timeout chan bool = make(chan bool)
    
    var WG sync.WaitGroup
    
    func Read() {
        for i := 0; i < 3; i++ {
            WG.Add(1)
        }
    }
    
    func Write() {
        for i := 0; i < 3; i++ {
            time.Sleep(time.Second * 2)
            fmt.Println("write i:", i)
            WG.Done()
        }
    }
    
    func main() {
        runtime.GOMAXPROCS(runtime.NumCPU())
        Read()
        go Write()
        WG.Wait()
        fmt.Println("all done!")
        time.Sleep(time.Second * 60)
    }
    
    
    // 输出
    MacBook-Pro:channel elasticnotes$ go run channel.go 
    write i: 0
    write i: 1
    write i: 2
    all done!
    

并发--总结

  • 如何启动携程
    • go
  • CPU的常规设置
    • go语言默认是所有核心数,但是可以依靠代码去设置核心数,通常是最大核心数-1
  • 携程间的通信及常规操作
    • 使用channel通信,接收数据可以使用select随机接收多个管道的数据
  • 如何控制协程间的同步
    • sync.WaitGroup

QA

进程

  • 进程是一种系统运行的行动
  • 进程的定位是程序的执行实体

线程

  • 线程是运算调度的最小单元
  • 线程的作用是同时运算多个任务

协程

  • 协程是轻量级的线程
  • 协程是用户态的线程
  • 协程的优势
    1. 协程的内存消耗更小
      1. 一个线程可以包含多个协程
      2. 线程大约8MB的内存申请量
      3. 协程大概2KB的内存申请量
    2. 上下文切换更快
      1. 协程少一道手续
      2. 线程申请内存,需要走过内核
      3. 协程申请内存,不需要走过内核

Goroutine的优势

  • 去掉了冗余的协程生命周期管理
    1. 协程创建
    2. 协程完成
    3. 协程重用
  • 降低额外的延迟和开销
    1. 由于协程之间频繁交互导致的
  • 降低加锁/解锁的频率
    1. 降低一部分额外的开销