Golang 语言控制并发 Goroutine的方法

goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。

01介绍

Golang 语言的优势之一是天生支持并发,我们在 Golang 语言开发中,通常使用的并发控制方式主要有 Channel,WaitGroup 和 Context,本文我们主要介绍一下 Golang 语言中并发控制的这三种方式怎么使用?关于它们各自的详细介绍在之前的文章已经介绍过,感兴趣的读者朋友们可以按需翻阅。

02Channel

在 Golang 语言中,Channel 不仅可以用于协程之间通信,还可以使用 Channel 控制子协程,而且使用 Channel 实现并发控制比较简单,比如以下示例,我们在 Golang 应用程序中启动两个协程,分别是主协程和子协程,主协程需要等待子协程运行结束后再退出程序。

示例代码:

func main () {
 done := make(chan struct{})
 go func() {
  fmt.Println("goroutine run over")
  done <- struct{}{}
 }()
 <- done
 fmt.Println("main goroutine run over")
} 

阅读上面这段代码,我们在子 goroutine 运行结束后,通过 Channel 通知主 goroutine 退出程序,实际上也可以反过来处理,主 goroutine 通知子 goroutine 退出程序,主 goroutine 向 channel 中发送数据,子 goroutine 等待接收 channel 中的数据。

03sync.WaitGroup

如果在 Golang 应用程序中,需要让主 goroutine 等待多个 goroutine 都运行结束后再退出程序,我们应该怎么实现呢?是的,同样可以使用 Channel 实现,但是,有一个更优雅的实现方式,那就是 WaitGroup,顾名思义,WaitGroup 就是等待一组 goroutine 运行结束。

示例代码:

func main () {
 wg := sync.WaitGroup{}
 wg.Add(10)
 for i := 0; i < 10; i++ {
  go func(id int) {
   fmt.Println(id, "运行结束")
   wg.Done()
  }(i)
 }
 wg.Wait()
 fmt.Println("main goroutine run over")
} 

阅读上面这段代码,我们启动 10 个子 goroutine,主 goroutine 需要等待 10 个子 goroutine 都运行结束后再退出程序,我们使用的是 WaitGroup,它有三个方法,分别是 Add、Done 和 Wait,实际上 WaitGroup 维护了一个计数器,这三个方法都是围绕这个计数器工作,Add 用于设置计数器的数值,Done 用于扣减计数器的数值,Wait 在计数器数值为 0 之前一直阻塞。关于 WaitGroup 的源码解读,在之前的文章中已介绍过,限于篇幅,这里就不再赘述。

04Context

Channel 和 WaitGroup 通常用于父子两个层级的 goroutine 的应用程序的并发控制中,如果在 Golang 应用程序中,子协程继续派生出协程,我们应该怎么控制呢?这种多级 goroutine 的应用程序,我们可以使用 Context 实现并发控制。

示例代码:

func main() {
 ctx, cancel := context.WithCancel(context.Background())
 go firstCtx(ctx)
 time.Sleep(5 * time.Second)
 fmt.Println("stop all sub goroutine")
 cancel()
 time.Sleep(5 * time.Second)
} 

func firstCtx(ctx context.Context) {
 go secondCtx(ctx)
 for {
  select {
  case <-ctx.Done():
   fmt.Println("first done")
   return
  default:
   fmt.Println("first running")
   time.Sleep(2 * time.Second)
  }
 }
} 

func secondCtx(ctx context.Context) {
 for {
  select {
  case <-ctx.Done():
   fmt.Println("second done")
   return
  default:
   fmt.Println("second running")
   time.Sleep(2 * time.Second)
  }
 }
} 

阅读上面这段代码,在子协程 firstCtx 启动子协程 secondCtx,主 goroutine 创建 context,并把 context 传递到所有子协程,然后主 goroutine 通过调用 cancle 停掉所有子协程。

05总结

本文我们介绍了不同场景中分别适合哪种控制并发 goroutine 的方式,其中,channel 适合控制少量 并发 goroutine,WaitGroup 适合控制一组并发 goroutine,而 context 适合控制多级并发 goroutine。

到此这篇关于Golang 语言控制并发 Goroutine的方法的文章就介绍到这了,更多相关Golang并发控制Goroutine内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang 探索对Goroutine的控制方法(详解)

    前言 在golang中,只需要在函数调用前加上关键字go即可创建一个并发任务单元,而这个新建的任务会被放入队列中,等待调度器安排.相比系统的MB级别线程栈,goroutine的自定义栈只有2KB,这使得我们能够轻易创建上万个并发任务,如此对性能提升不少.但随之而来的有以下几个问题: 如何等待所有goroutine的退出 如何限制创建goroutine的数量(信号量实现) 怎么让goroutine主动退出 探索--如何从外部杀死goroutine 本文记录了笔者就以上几个问题进行探究的过程,文中给

  • golang gin 框架 异步同步 goroutine 并发操作

    goroutine机制可以方便地实现异步处理 package main import ( "log" "time" "github.com/gin-gonic/gin" ) func main() { // 1.创建路由 // 默认使用了2个中间件Logger(), Recovery() r := gin.Default() // 1.异步 r.GET("/long_async", func(c *gin.Context) {

  • 关于Golang中for-loop与goroutine的问题详解

    背景 最近在学习MIT的分布式课程6.824的过程中,使用Go实现Raft协议时遇到了一些问题.分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 参见如下代码: for i := 0; i < len(rf.peers); i++ { DPrintf("i = %d", i) if i == rf.me { DPrintf("skipping myself #%d", rf.me) continue } go func() { DPrintf(

  • golang goroutine顺序输出方式

    range字符串,使用goroutine打印 因为goroutine随机执行 for _, v := range str { go func() { fmt.Println(string(v)) }() } 输出: 5 5 5 5 5 可以使用chan顺序输出 for _, c := range str{ ch := make(chan rune) go func(ch <-chan rune) { key := <-ch fmt.Println(string(key)) }(ch) ch &

  • GOLANG使用Context管理关联goroutine的方法

    一般一个业务很少不用到goroutine的,因为很多方法是需要等待的,例如http.Server.ListenAndServe这个就是等待的,除非关闭了Server或Listener,否则是不会返回的.除非是一个API服务器,否则肯定需要另外起goroutine发起其他的服务,而且对于API服务器来说,在http.Handler的处理函数中一般也需要起goroutine,如何管理这些goroutine,在GOLANG1.7提供context.Context. 先看一个简单的,如果启动两个goro

  • 解决Golang中goroutine执行速度的问题

    突然想到了之前一直没留意的for循环中开goroutine的执行顺序问题,就找了段代码试了试,试了几次后发现几个有意思的地方,我暂时没有精力往更深处挖掘,希望有golang大神能简单说一说这几个地方是怎么回事. 代码: package main import "fmt" func Count(ch chan int) { fmt.Println("Count doing") ch <- 1 fmt.Println("Counting") }

  • Golang 语言控制并发 Goroutine的方法

    goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理.Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU. 01介绍 Golang 语言的优势之一是天生支持并发,我们在 Golang 语言开发中,通常使用的并发控制方式主要有 Channel,WaitGroup 和 Context,本文我们主要介绍一下 Golang 语言中并发控制的这三种方式怎么使用?关于它们各自的详细介绍在之前的文章已经介绍过,感兴趣的读者朋友们可以按需翻阅. 02

  • Golang 语言高效使用字符串的方法

    01介绍 在 Golang 语言中,string 类型的值是只读的,不可以被修改.如果需要修改,通常的做法是对原字符串进行截取和拼接操作,从而生成一个新字符串,但是会涉及内存分配和数据拷贝,从而有性能开销.本文我们介绍在 Golang 语言中怎么高效使用字符串. 02字符串的数据结构 在 Golang 语言中,字符串的值存储在一块连续的内存空间,我们可以把存储数据的内存空间看作一个字节数组,字符串在 runtime 中的数据结构是一个结构体 stringStruct,该结构体包含两个字段,分别是

  • 使用async、enterproxy控制并发数量的方法详解

    聊聊并发与并行 并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行. 并发我们经常提及之,不管是web server,app并发无处不在,操作系统中,指一个时间段中几个程序处于已经启动运行到完毕之间,且这几个程序都是在同一处理机上运行,并且任一个时间点只有一个程序在处理机上运行.很多网站都有并发连接数量的限制,所以当请求发送太快的时候会导致返回值为空或报错.更有甚者,有些网站可能因为你发出

  • Go语言CSP并发模型goroutine channel底层实现原理

    目录 Go的CSP并发模型(goroutine + channel) 1.goroutine goroutine的优点: 2.channel 无缓存channel 有缓存channel 3.Go并发模型的底层实现原理 4.一个CSP例子 参考Go的CSP并发模型实现:M, P, G Go语言是为并发而生的语言,Go语言是为数不多的在语言层面实现并发的语言. 并发(concurrency):多个任务在同一段时间内运行. 并行(parallellism):多个任务在同一时刻运行. Go的CSP并发模

  • golang通过context控制并发的应用场景实现

    golang 里出现多 goroutine 的场景很常见, 最常用的两种方式就是 WaitGroup 和 Context, 今天我们了解一下 Context 的应用场景 使用场景 场景一: 多goroutine执行超时通知 并发执行的业务中最常见的就是有协程执行超时, 如果不做超时处理就会出现一个僵尸进程, 这累计的多了就会有一阵手忙脚乱了, 所以我们要在源头上就避免它们 看下面这个示例: package main import ( "context" "fmt"

  • Go语言通过WaitGroup实现控制并发的示例详解

    目录 与Channel区别 基本使用示例 完整代码 特别提示 多任务示例 完整代码 与Channel区别 Channel能够很好的帮助我们控制并发,但是在开发习惯上与显示的表达不太相同,所以在Go语言中可以利用sync包中的WaitGroup实现并发控制,更加直观. 基本使用示例 我们将之前的示例加以改造,引入sync.WaitGroup来实现并发控制. 首先我们在主函数中定义WaitGroup var wg sync.WaitGroup 每执行一个任务,则调用Add()方法 wg.Add(1)

  • MySQL 加锁控制并发的方法

    前言 锁总体可以分为乐观锁和悲观锁,简单说,乐观锁用版本号控制,悲观锁用锁控制. 下面是待会要用来测试的数据 # 添加一个user表 CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(255) NOT NULL COMMENT '姓名', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSE

  • Go语言中的并发goroutine底层原理

    目录 一.基本概念 ①并发.并行区分 ②从用户态线程,内核态线程阐述go与java并发的优劣 ②高并发为什么是Go语言强项? ③Go语言实现高并发底层GMP模型原理解析 二.上代码学会Go语言并发 ①.开启一个简单的线程 ②.动态的关闭线程 一.基本概念 ①并发.并行区分 1.概念 并发:同一时间段内一个对象执行多个任务,充分利用时间 并行:同一时刻,多个对象执行多个任务 2.图解 类似于超市柜台结账,并行是多个柜台结多个队列,在计算机中是多核cpu处理多个go语言开启的线程,并发是一个柜台结账

  • node 使用 async 控制并发的方法

    目标 建立一个 lesson5 项目,在其中编写代码. 代码的入口是 app.js,当调用 node app.js 时,它会输出 CNode(https://cnodejs.org/ ) 社区首页的所有主题的标题,链接和第一条评论,以 json 的格式. 注意:与上节课不同,并发连接数需要控制在 5 个. 输出示例: [ { "title": "[公告]发招聘帖的同学留意一下这里", "href": "http://cnodejs.or

  • 详解Golang 中的并发限制与超时控制

    前言 上回在 用 Go 写一个轻量级的 ssh 批量操作工具里提及过,我们做 Golang 并发的时候要对并发进行限制,对 goroutine 的执行要有超时控制.那会没有细说,这里展开讨论一下. 以下示例代码全部可以直接在 The Go Playground上运行测试: 并发 我们先来跑一个简单的并发看看 package main import ( "fmt" "time" ) func run(task_id, sleeptime int, ch chan st

随机推荐