当前位置: 首页 > news >正文

Golang并发控制的三种方案

Channel

Channel是Go在语言层面提供的一种协程间的通信方式,我们可以通过在协程中向管道写入数据和在待等待的协程中读取对应协程的次数来实现并发控制。

func main() {intChan := make(chan int, 5)waitCount := 5for i := 0; i < waitCount; i++ {go func() {intChan <- 1}()}for i := 0; i < waitCount; i++ {<-intChan}fmt.Println("主进程结束")
}

WaitGroup

waitgroup通常应用于等待一组“工作协程”结束的场景,waitgroup底层是由一个长度为3的数组实现的,其内部有两个计数器,一个是工作协程计数器、一个是坐等协程计数器,还有一个是信号量。工作协程全部运行结束后,工作协程计数器将置为0,会释放对应坐等协程次数的信号量。

两点注意:

  1. Add()方法中的参数大小要于工作协程的数量相等,否则会导致坐等协程一直等待,触发死锁panic

  2. Done()方法执行的次数要与Add()方法中的工作协程计数器的数量一致,否则当工作协程计数器<0时,会触发panic【panic: sync: negative WaitGroup counter】

func main() {wg := sync.WaitGroup{}wg.Add(2)go func() {time.Sleep(3 * time.Second)fmt.Println("等待三分钟的协程结束了")wg.Done()}()go func() {time.Sleep(3 * time.Second)fmt.Println("等待三分钟的协程结束了")wg.Done()}()wg.Wait()
}

Context

适用于一个协程派生出多个协程的情况,可以控制多级的goroutine。我们可以通过一个Context对象,对派生出来的树状goroutine进行统一管理,并且每个goroutine具有相同的上下文。做统一关闭操作、统一定时关闭、统一传值的操作。多个上下文协程之间可以互相嵌套配合。

golang实现了四种原生的上下文对象

  • emptyCtx: 该上下文对象一般是作为父节点的,如果没有父节点,我们通常使用context.Background()方法来获取emptyCtx对象,并将其作为创建其他节点的父节点。

  • cancelCtx: 该上下文对象可以关闭所有拥有同一个上下文的goroutine,通过在子协程中监听cancelCtx.Done方法,来结束所有的派生协程。具体代码看下方,我们通过WithCancel()方法来获取该对象。

  • timerCtx:该上下文对象是对cancelCtx对象的进一步封装,比cancelCtx主动关闭之外,多了了一个定时关闭功能。我们可以通过WithTimeout()和WithDeadline()这两种方法来获取该对象。其中WithTimeout()和WithDeadline()这两种方法点是WithTimeout()是设置过一段时间关闭上下文,WithDeadline()是设置那一个时间点来关闭这一个上下文。

  • valueCtx:该上下文对象并不用于进行协程的控制,而是在多级协程之间进行值得传递,方便共享一些相同得上下文内容。

以上除emptyCtx外的上下文对象和获取实例的方法如下图所示:

 Context示例代码

  • cancelCtx

        我们在所有的派生协程中传入相同的cancelContext对象,并在每一个子协程中使用switch-case结构监听上下文对象是否关闭,如果上下文对象关闭了,ctx.Done()返回的管道就可以读取到一个元素,使所在的case语句可执行,之后退出switch结构,执行协程中的其他代码。

func main() {ctx, cancelFunc := context.WithCancel(context.Background())deadline, ok := ctx.Deadline()fmt.Println(deadline, ok)done := ctx.Done()fmt.Println(reflect.TypeOf(done))fmt.Println(done)go HandelRequest(ctx)//<-done 阻塞当前一层的goroutinetime.Sleep(5 * time.Second)fmt.Println("all goroutines is stopping!")cancelFunc()err := ctx.Err()fmt.Println(err) //context canceledtime.Sleep(5 * time.Second)
}func HandelRequest(ctx context.Context) {go WriteMysql(ctx)go WriteRedis(ctx)for {select {case <-ctx.Done():fmt.Println("HandelRequest Done")returndefault:fmt.Println("等一等,Handler正在执行中")time.Sleep(2 * time.Second)}}
}func WriteRedis(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteRedis Done.")returndefault:fmt.Println("等一等,Redis正在执行中")time.Sleep(2 * time.Second)}}
}func WriteMysql(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteMysql Done.")returndefault:fmt.Println("等一等,Mysql正在执行中")time.Sleep(2 * time.Second)}}
}
  • timerCtx

        这里代码以WithTimeout举例,相比与我们之前的手动调用关闭,使用timerCtx定时上下文对象后,可以是实现到达指定的时间自动进行关闭的操作。

func main() {deadline, _ := context.WithTimeout(context.Background(), 5*time.Second)go HandelRequest(deadline)time.Sleep(10 * time.Second)}func HandelRequest(ctx context.Context) {go WriteMysql(ctx)go WriteRedis(ctx)for {select {case <-ctx.Done():fmt.Println("HandelRequest Done")returndefault:fmt.Println("等一等,Handler正在执行中")time.Sleep(2 * time.Second)}}
}func WriteRedis(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteRedis Done.")returndefault:fmt.Println("等一等,Redis正在执行中")time.Sleep(2 * time.Second)}}
}func WriteMysql(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteMysql Done.")returndefault:fmt.Println("等一等,Mysql正在执行中")time.Sleep(2 * time.Second)}}
}
  • valueCtx

        我们可以通过嵌套WithValue上下文,来进行多个key-value在派生协程中传递,共享常量

func main() {ctx, cancelFunc := context.WithCancel(context.Background())value1 := context.WithValue(ctx, "param1", 1)value2 := context.WithValue(value1, "param2", 2)go ReadContextValue(value2)time.Sleep(10 * time.Second)cancelFunc()time.Sleep(5 * time.Second)
}func ReadContextValue(ctx context.Context) {fmt.Println(ctx.Value("param1"))fmt.Println(ctx.Value("param2"))
}

相关文章:

  • 一文理清GO语言日志库实现开发项目中的日志功能(rotatelogs/zap分析)
  • 基于多头注意力机制卷积神经网络结合双向门控单元CNN-BIGRU-Mutilhead-Attention实现柴油机故障诊断附matlab代码
  • MongoDB~高可用集群介绍:复制集群(副本集)、分片集群
  • SQL MAX() 函数深入解析
  • PyQt5设计登录跳转界面
  • 使用net.sf.mpxj读取project的.mpp文件
  • 文件操作(2)(C语言版)
  • Tuple 元组
  • MAX30102驱动
  • 【ajax基础03】常用ajax请求方法和数据提交以及axios错误处理
  • Java23种设计模式(二)
  • MySQL系列-语法说明以及基本操作(二)
  • 力扣爆刷第153天之TOP100五连刷(相交、翻转、排序链表、螺旋矩阵、锯齿二叉树)
  • IPython 使用技巧整理
  • Linux系统之mtr命令的基本使用
  • [PHP内核探索]PHP中的哈希表
  • 《深入 React 技术栈》
  • 07.Android之多媒体问题
  • CentOS7简单部署NFS
  • Druid 在有赞的实践
  • es的写入过程
  • express如何解决request entity too large问题
  • FineReport中如何实现自动滚屏效果
  • java8-模拟hadoop
  • js算法-归并排序(merge_sort)
  • Vim Clutch | 面向脚踏板编程……
  • 搭建gitbook 和 访问权限认证
  • 大数据与云计算学习:数据分析(二)
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 极限编程 (Extreme Programming) - 发布计划 (Release Planning)
  • 将回调地狱按在地上摩擦的Promise
  • 前端之React实战:创建跨平台的项目架构
  • 如何使用 JavaScript 解析 URL
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 小而合理的前端理论:rscss和rsjs
  • 学习ES6 变量的解构赋值
  • raise 与 raise ... from 的区别
  • 新海诚画集[秒速5センチメートル:樱花抄·春]
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • #NOIP 2014#Day.2 T3 解方程
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (SpringBoot)第七章:SpringBoot日志文件
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (转)视频码率,帧率和分辨率的联系与区别
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .apk文件,IIS不支持下载解决
  • .Net MVC4 上传大文件,并保存表单
  • .net mvc部分视图
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • @EnableAsync和@Async开始异步任务支持
  • @SpringBootApplication 包含的三个注解及其含义
  • [AIR] NativeExtension在IOS下的开发实例 --- IOS项目的创建 (一)