Go语言中的并发控制| 青训营笔记

102 阅读3分钟

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

前言

当谈到Go语言中的并发性时,Go语言的一个特性goroutine在日常开发中被大量使用。

启动goroutine非常简单,只需在函数之前添加关键字go。因为每个goroutine都是独立运行的,所以它的退出是由它自己决定的,除非主程序终止或崩溃。

那么,如何控制goroutine或告诉goroutine停止运行呢?

解决办法很简单。找到一种与goroutine通信的方法,并让他们知道goroutine何时完成。当goroutine完成时,可以通知另一个goroutine或主程序。

channel

channel是goroutine之间的主要通信方法,通常与select结合使用。

  1. 宣布一个stopchan。

2、在goroutine中,使用select来判断'stop'是否可以接收到值,如果可以接收到,就意味着可以退出停止;如果没有接收到,则执行'default'中的逻辑。直到收到“停止”通知为止。

3、主程序发送'stop< -true'结束指令。

4,子goroutine接收到结束指令case <-stop exit return。

示例

package main

import (
   "fmt"
   "time"
)

func main() {
   stop := make(chan bool)
   go func() {
      for {
         select {
         case <-stop:
            fmt.Println("goroutine exit")
            return
         default:
            fmt.Println("goroutine running")
            time.Sleep(1 * time.Second)
         }
      }
   }()
   time.Sleep(2 * time.Second)
   stop <- true
   time.Sleep(2 * time.Second)
   fmt.Println("main fun exit")
}

输出

goroutine running
goroutine running
goroutine running
goroutine exit
main fun exit

这种select+chan是一种比较优雅的并发控制方式,但也有局限性,如多个goroutine 需要结束,以及嵌套goroutine 的场景。

WaitGroup

Go语言提供了同步包(sync),源代码(src/sync/waitgroup.go)。 Sync包提供了基本的同步原语,比如互斥。除了Once和WaitGroup类型外,大多数类型都由低级库例程使用。通过通道和通信可以更好地实现更高级别的同步。并且这个包中的值在使用后不应该被复制。

同步。WaitGroup是一种实现并发控制的方法。WaitGroup对象内部有一个计数器,从0开始。它有三个方法:'Add(), Done(), Wait()'来控制计数器的数量。

  • 'Add(n)'设置计数器为n '。
  • 'Done()'每次将计数器更改为-1。
  • 'wait()'阻塞代码,直到计数器减少到零。 例子
package main

import (
   "fmt"
   "sync"
   "time"
)

func main() {
   //定义一个WaitGroup
   var wg sync.WaitGroup
   //计数器设置为2
   wg.Add(2)
   go func() {
      time.Sleep(2 * time.Second)
      fmt.Println("goroutineA finish")
      //计数器减1
      wg.Done()
   }()
   go func() {
      time.Sleep(2 * time.Second)
      fmt.Println("goroutineB finish")
      //计数器减1
      wg.Done()
   }()
   //会阻塞代码的运行,直到计数器地值减为0。
   wg.Wait()
   time.Sleep(2 * time.Second)
   fmt.Println("main fun exit")
}

当有多个goroutine一起工作来做某件事时,这种控制并发性的方式非常有用,因为每个goroutine都在做某件事的一部分,直到所有goroutine都完成它,它才会完成,这就是等待的方式。WaitGroup比通道并发控制更轻。

总结

在Golang中并没有好的或坏的并发行为控制模型,只是针对不同场景的适当解决方案。在实际项目中,经常混合使用多种方法。

  • WaitGroup:多个goroutine的任务相互依赖或拼接。
  • channel:取消goroutine。多路程序数据传输;通道可以完成WaitGroup的工作,但增加了代码逻辑复杂性