zoukankan      html  css  js  c++  java
  • Go 并发编程机制

    学习-极客时间-Go语言从入门到实战 笔记

    并发是Golang的核心和优势,掌握它能提高编程效率。有三种基本机制来实现:协程机制,共享内存并发机制和CSP机制。

    协程机制

    GO 的协程(Goroutine)和系统线程(Thread M)之间有go特有的调度器(Processor),实现协程的挂起和调度,调度也可以将自己切换到别的线程。下面这张图比较简洁:

    代码片段举例:

    func TestGoroutine(t *testing.T) {
      for i := 0;i < 10 ;i++  {
        go func(i int) {
          fmt.Println(i)
        }(i)  //要将参数复制到协程,才能运行正确
      }
    }
    

    共享内存并发机制

    协程间共用变量的时候要特别注意,我们可以使用锁的方式和waitGroup来解决。

    代码片段举例:

    package share_mem
    
    import (
      "sync"
      "testing"
      "time"
    )
    
    //用一个计数器举例 这样编写没有办法得到正确结果,
    //因为count变量在并发时没有按期望递增
    func TestShareMem(t *testing.T) {
      count := 0
      for i:=0;i <5000 ;i++  {
        go func() {
          count++
        }()
      }
      time.Sleep(1 * time.Second)
      t.Log(count)
    }
    
    //加上锁之后 可以保证共享变量的正确传递
    func TestShareMemSafe(t *testing.T) {
      var mut sync.Mutex
      count :=0
      for i := 0; i < 5000; i++ {
        go func() {
          defer func() {
            mut.Unlock()
          }()
          mut.Lock()
          count++
        }()
      }
      //但是没有给到充足的运行时间,程序还是可能会提前结束,导致计算错误
      time.Sleep(1 * time.Second)
      t.Log(count)
    }
    
    //更好的办法是使用 waitGroup
    func TestShareMemWaitGroup(t *testing.T) {
      var w sync.WaitGroup
      var mut sync.Mutex
      count := 0
      for i := 0; i < 5000; i++ {
        w.Add(1)
        go func() {
          defer func() {
            mut.Unlock()
          }()
          mut.Lock()
          count++
          w.Done()
        }()
      }
      w.Wait()
      t.Log(count)
    }
    

    CSP并发机制

    可以在协程中使用通道(channel)传递结果,达成异步运行程序。通道可指定容量,这样不会阻塞协程,消息发送和接收解耦。
    没有指定容量,通道会阻塞:
    没有指定容量的channel
    指定容量,发送与接收解耦:

    代码片段举例:

    package csp
    
    import (
      "fmt"
      "testing"
      "time"
    )
    
    //普通任务
    func Service() string {
      time.Sleep(time.Millisecond * 50)
      return "task is done"
    }
    
    //其他任务
    func otherTask() {
      fmt.Println("Other task start")
      time.Sleep(time.Millisecond * 100)
      fmt.Println("Other task end")
    }
    
    //异步任务
    func AsyncService() chan string {
      retCh := make(chan string)
      //retCh := make(chan string, 1) 指定Chanel容量后,可加快效率,无需等待Chanel被拿走后才能执行其他任务
      go func() {
        res := Service()
        fmt.Println("return result")
        retCh <- res
        //没有指定容量,goroutine 会被阻塞
        fmt.Println("service exit...")
      }()
      return retCh
    }
    
    //测试同步任务
    func TestSyncTask(t *testing.T) {
      fmt.Println(Service())
      otherTask()
      // 耗时 0.15s
    }
    
    //测试异步任务
    func TestAsyncTask(t *testing.T) {
      retCh := AsyncService()
      otherTask()
      fmt.Println(<-retCh)
      // 耗时 0.10s
    }
    
  • 相关阅读:
    python3爬虫-快速入门-爬取图片和标题
    数据库Mysql的学习(八)-储存过程和事务和导入导出
    数据库Mysql的学习(七)-自定义函数和流程控制
    git reset --hard HEAD^
    list采坑记录一下
    Linux运行jar包
    JSONObject.toJSONString(map)
    String转list
    判断list中元素是否是相邻
    统计List中相同的元素
  • 原文地址:https://www.cnblogs.com/yangqi7/p/12425430.html
Copyright © 2011-2022 走看看