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
}
posted @ 2020-03-06 11:05  yangqi7  阅读(216)  评论(0编辑  收藏  举报