《7天学会Go并发编程》第五天 go语言Context的应用和实现 go实现超时判断

本文是《7天学会Go并发编程》系列的总结,详细介绍了Go并发编程中的Context和Atomic操作。通过案例展示了如何使用Context实现线程的超时取消、限制线程启动次数以及层级传递,同时探讨了Atomic在并发环境中的原子操作和ABA问题。
摘要由CSDN通过智能技术生成

  系列文章目录

《7天学会Go并发编程》第一天 go并发实战初窥门径_大锤爱编程的博客-CSDN博客😯编程就是手艺活,看了就要练,看了就要写。不写不练浪费时间撒。🚗🚗🚗🚗今天先搞起一个Go当中的并发编程处理。一、Go当中常用并发二、Go 并发案例基本使用三、个人总结https://blog.csdn.net/alike_u/article/details/124056852?spm=1001.2014.3001.5502

《7天学会Go并发编程》の第二天 写一个简单的Go并发程序以及Goroutine的使用_大锤爱编程的博客-CSDN博客一文学会go线程的常见应用https://blog.csdn.net/alike_u/article/details/124237576?spm=1001.2014.3001.5502

《7天学会Go并发编程》第三天 go的Mutex使用_大锤爱编程的博客-CSDN博客😯go语言并发编程中,简单经典好用的就是Mutex,也就是go语言中的互斥,适用于比较初阶的go并发编程。🚗🚗🚗文章里面会介绍Mutex的两个经典使用方法。目录一、go语言并发简介二、go语言并发小试牛刀2.1 原生态并发计数2.1 遇事不决,加把锁2.2 加锁慢又麻,原子操作好快省2.3channel,go并发灵魂三、总结一、Mutex是什么Mutex是go语言当中的互斥锁,作用是用来给临界区的数据加锁,保证一次只有一个goroutline可以对..https://blog.csdn.net/alike_u/article/details/124463044?spm=1001.2014.3001.5502

《7天学会Go并发编程》第四天 sync.Once和sync.WaitGroup学习应用实践_大锤爱编程的博客-CSDN博客😯并发编程当总是会遇到一些线程调度的问题。有A,B,C,D四个线程,需要确保在A,B,C执行结束后,再执行D线程。比如写一段Fan-In,多表融合的代码,A,B,C从独立的三表中抽取出数据,在线程D中进行融合。那么在这个过程中,如果ABC线程数据抽取未完成,D就开始进行数据融合,势必造成数据融合结果的缺失,不完整。 单线程的单次执行,也就是说这个线程只能执行一次。比如循环判断一个条件是否满足,满足以后执行单次执行,避免重复执行。例如金额的累加,判断累加值以后,不论成功与否...https://blog.csdn.net/alike_u/article/details/124569174?spm=1001.2014.3001.5502

《7天学会Go并发编程》第五天 go语言Context的应用和实现 go实现超时判断_大锤爱编程的博客-CSDN博客系列文章目录提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:第一章 Python 机器学习入门之pandas的使用提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录系列文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结前言提示:这里可以添加本文要记录的大概内容:例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的https://blog.csdn.net/alike_u/article/details/124721223?spm=1001.2014.3001.5502

《7天学会Go并发编程》第六天 go语言Sync.cond的应用和实现 go实现多线程联合执行_大锤爱编程的博客-CSDN博客CSDN话题挑战赛第1期活动详情地址:CSDN参赛话题:大数据学习记录话题描述:大数据生态圈内的理念和技术实现,包括但不局限于大数据存储(数据湖、数据仓库、消息队列等)、大数据计算(离线批、实时流等)、大数据应用(报表可视化、OLAP应用、用户画像、智能搜索、智能推荐等)、大数据的调度(各类调度器)等方面;囊括开源社区(如Apache、CDH、HDP、等)大数据技术栈以及公有云(如AWS、阿里云、华为云、腾讯云、Microsoft Azure)大数据技术栈,这里是大数据的阵地,让各类大数据理念在此百家https://blog.csdn.net/alike_u/article/details/124677244?spm=1001.2014.3001.5502

《7天学会Go并发编程》第7天 go语言并发编程Atomic原子实战操作含ABA问题_大锤爱编程的博客-CSDN博客目录一、Atomic-原子操作简介二、🍻go语言Atomic源码分析和实战操作2.1🍺🍺🍺 Atomic 关键函数及其释义2.1 🍭🍭🍭atomic实战2.2 🍖🍖🍖atomic原子并发操作2.3 🍟🍟🍟任意数据结构的原子操作三、🍏🍏🍏总结原子操作其实和原子没有什么关系,主要借用的是原子不可分割的概念来强调说明这个操作也不可以被分割。🚗使用并发编程时需要解决的就是临界区(公共资源的修改)问题,抽象的数据操作行为是RAW,WAR,WAW。🥤🥤🥤体现在具体编码过程中,就是...https://blog.csdn.net/alike_u/article/details/125349003?spm=1001.2014.3001.5502

目录​​​​

前言

一、Context是什么?

二、Context使用案例

1.使用Context CancelFunc限制线程启动次数

2.Derived Context

3 使用Context实现超时取消

4 使用Context实现主动超时取消

总结



前言

        前面的文章主要介绍了sync.WaitGroup类型:主要讲如何通过waitGroup实现一对多的goroutline协作流程。通俗的说就是,使用一个线程等待其他线程完成。

waitGroup:主要的使用方法:1、先add需要等待的线程数 2、主线程阻塞,等待并发线程执行3、主线程阻塞等待并发线程执行完毕

使用waitGroup的劣势:

        1、只能实现一对多的线程等待,不能支撑嵌套的层次等待

        2、使用waitGroup时,需要注意并发线程数和线程等待数一致,不一致的话,就会引起panic

         针对上述问题,go预言提出了context,实现线程数不定的多线程阻塞等待,可以实现多线程的依赖等待。


一、Context是什么?

        Context类型是context包的核心类型,能够在函数调用的过程之中传递结束信息,请求范围的值、截止时间。

        代码如下:其中Done返回一个信号结束的channel,Deadline返回一个任务停止执行的截止日期,Value则返回一个可以被多个线程安全使用的Value。  

// A Context carries a deadline, cancellation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

二、Context使用案例

1.使用Context CancelFunc限制线程启动次数

使用Context限制线程开启次数,下面的代码实现线程开启12个后,便主动结束所有线程。

func main() {
	contextExample()
}

func contextExample() {
	total := 12
	var num int32
	fmt.Printf("The number: %d [with context.Context]\n", num)
	cxt, cancelFunc := context.WithCancel(context.Background())
	for i := 1; i <= total; i++ {
		go addNum1(&num, i, func() {
			if atomic.LoadInt32(&num) == int32(total) {
				cancelFunc()
			}
		})
	}
	<-cxt.Done()
	fmt.Println("End.")
}


func addNum1(numP *int32, id int, deferFunc func()) {

	for i := 0; ; i++ {
		deferFunc()
		time.Sleep(time.Millisecond * 200)
		atomic.AddInt32(numP,1)
		fmt.Printf("累计线程启动次数: %d 线程序号:%d-打印值:%d\n", atomic.LoadInt32(numP), id, i)
	}
}

2.Derived Context

go语言的实际开发过程中,context也是可以传递的。从parentContext向下看,完整Context形成一颗多叉树。

前往讲过Context是可以层级取消的,这里主要距离说明一下Context的衍生或者Derived案例。

package main

import (
	"context"
	"fmt"
	"sync/atomic"
	"time"
)

func main() {
	layer1 := context.WithValue(context.Background(), "key1", "layer:layer1 v->value1")
	layer2 := context.WithValue(layer1, "key2", "layer:layer2 v->value2")
	value1 := layer2.Value("key1")
	value2 := layer2.Value("key2")
	fmt.Print(value1)
	fmt.Println()
	fmt.Println(value2)
}

 输出如下,layer2作为layer1衍生出来的context,携带了这个分支上所有的值,通过Value函数的方式获取了属于layer1的value值。


layer:layer1 v->value1
layer:layer2 v->value2

Process finished with the exit code 0

3 使用Context实现超时取消

超时取消如何实现呢?实际开发过程中,接口超时断开是一个常见功能。那么在go语言中如何实现呢?

主线程在func1启动,睡眠2S后,执行ctx取消。func1当中也有自身的慢执行停止逻辑,出于演示 ,让主线程代码先停止。实际应用过程中,可根据需求来进行相应编程。

package main

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

func func1(ctx context.Context, wg *sync.WaitGroup) error {
	defer wg.Done()
	respC := make(chan int)
	// 处理逻辑
	go func() {
		time.Sleep(time.Second * 5)
		respC <- 10
	}()
	// 取消机制
	select {
	//等于ctx的超时退出
	case <-ctx.Done():
		fmt.Println("cancel")
		return nil
	//等待睡眠5S的线程退出	
	case r := <-respC:
		fmt.Println(r)
		return nil
	}
}

func main() {
	wg := new(sync.WaitGroup)
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	go func1(ctx, wg)
	time.Sleep(time.Second * 2)
	// 触发取消
	cancel()
	// 等待goroutine退出
	wg.Wait()
}

4 使用Context实现主动超时取消

func main() {
	timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*10)
	defer cancelFunc() // releases resources if slowOperation completes before timeout elapses
	slowOperation(timeout)
	fmt.Println("都停止了")
}

func slowOperation(ctx context.Context) {
	go func() {
		for i := 0; ; i++ {
			time.Sleep(1*time.Second)
			fmt.Printf("%s 肆意打印:%d\n",time.Now(),i)
		}
	}()
	select {
	case <-ctx.Done():
		fmt.Println("deadline已到")
	}
}

对应的结果:可以看出一个执行死循环的go线程活了十秒以后,被强制终结。

2022-05-14 16:47:56.7303913 +0800 CST m=+1.020835201 肆意打印:0
2022-05-14 16:47:57.8223593 +0800 CST m=+2.112803201 肆意打印:1
2022-05-14 16:47:58.8311153 +0800 CST m=+3.121559201 肆意打印:2
2022-05-14 16:47:59.8427716 +0800 CST m=+4.133215501 肆意打印:3
2022-05-14 16:48:00.8550931 +0800 CST m=+5.145537001 肆意打印:4
2022-05-14 16:48:01.8710894 +0800 CST m=+6.161533301 肆意打印:5
2022-05-14 16:48:02.8788328 +0800 CST m=+7.169276701 肆意打印:6
2022-05-14 16:48:03.8899483 +0800 CST m=+8.180392201 肆意打印:7
2022-05-14 16:48:04.9039227 +0800 CST m=+9.194366601 肆意打印:8
deadline已到
都停止了

总结

         context 的实现相对来说比较复杂,但在实际使用中能给大家带来不小的便利。本篇博客结合博主自身的使用经验,介绍一下Context的使用方法和使用场景。欢迎大家对我的博客提出宝贵建议和真诚评论。

 

评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大锤爱编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值