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
    }
    
  • 相关阅读:
    现在的代码,贴一下
    2014年七月写过的代码,现在看来,还有待改进呀
    第一次做技术论坛发博文,不知道说点啥
    04-树6. Huffman Codes--优先队列(堆)在哈夫曼树与哈夫曼编码上的应用
    04-树5. File Transfer--并查集
    04-树4. Root of AVL Tree-平衡查找树AVL树的实现
    03-树3. Tree Traversals Again (25)将先序遍历和中序遍历转为后序遍历
    03-树2. List Leaves (25) 二叉树的层序遍历
    二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历
    最大子序列和问题之算法优化
  • 原文地址:https://www.cnblogs.com/yangqi7/p/12425430.html
Copyright © 2011-2022 走看看