Golang并发控制的三种方案

news2025/1/18 14:03:59

Channel

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

func main() {
    intChan := make(chan int, 5)
    waitCount := 5
    for 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 阻塞当前一层的goroutine
	time.Sleep(5 * time.Second)
	fmt.Println("all goroutines is stopping!")
	cancelFunc()
	err := ctx.Err()
	fmt.Println(err) //context canceled
	time.Sleep(5 * time.Second)
}

func HandelRequest(ctx context.Context) {
	go WriteMysql(ctx)
	go WriteRedis(ctx)
	for {
		select {
		case <-ctx.Done():
			fmt.Println("HandelRequest Done")
			return
		default:
			fmt.Println("等一等,Handler正在执行中")
			time.Sleep(2 * time.Second)
		}
	}
}

func WriteRedis(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteRedis Done.")
			return
		default:
			fmt.Println("等一等,Redis正在执行中")
			time.Sleep(2 * time.Second)
		}
	}
}

func WriteMysql(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteMysql Done.")
			return
		default:
			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")
			return
		default:
			fmt.Println("等一等,Handler正在执行中")
			time.Sleep(2 * time.Second)
		}
	}
}

func WriteRedis(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteRedis Done.")
			return
		default:
			fmt.Println("等一等,Redis正在执行中")
			time.Sleep(2 * time.Second)
		}
	}
}

func WriteMysql(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteMysql Done.")
			return
		default:
			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"))
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1848081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络体系结构】计算机网络体系结构实验-FTP实验

1. 2. 3. wireshark 第一行&#xff1a;帧Frame 545&#xff1a;要发送的数据块&#xff0c;所抓帧的序号为545&#xff0c;捕获字节数等于传送字节数&#xff1a;451字节第二行&#xff1a;源Mac地址为a4:bb:6d:6e:28:9a&#xff1b;目标Mac地址为24:00:fa:e4:df:d8第三行&…

安卓逆向案例——X酷APP逆向分析

X酷APP逆向分析 这里介绍一下两种不同的挂载证书的方法。 chls.pro/ssl无法在浏览器中下载证书是什么原因解决方法&#xff1a; 法一 1. 挂载系统分区为读写 使用正确的挂载点来挂载系统分区为读写&#xff1a; su mount -o remount,rw /dev/uijISjR/.magisk/block/syste…

Spring Boot连接Redis集群

1、问题写在前面 1.1、问题描述&#xff1a;Redis集群节点地址发现失败 Unable to connect to [172.17.0.4:7303]: connection timed out: /172.17.0.4:7303 1.2、解决方案&#xff1a; redis.conf 中添加配置 cluster-announce-ip 192.168.56.11 1.3、方案出处&#xff1a;…

何在 Vue3 中使用 Cytoscape.js 创建交互式网络图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 Cytoscape.js集成到Vue应用中 应用场景介绍 Cytoscape.js是一个用于可视化复杂网络数据的JavaScript库。它提供了丰富的功能&#xff0c;包括节点和边的创建、布局算法、交互式操作等。本文将介绍如何在Vue应…

如何将办公文档压缩成rar格式文件?

压缩包格式是我们生活工作中常用到的文件格式&#xff0c;那么如何得到一个rar格式的压缩文件&#xff1f;或者说如何将文件压缩成rar格式而不是zip格式呢&#xff1f;今天我们来了解一下如何压缩为rar格式文件。 首先&#xff0c;下载并安装WinRAR&#xff0c;然后用鼠标选择需…

Flat Ads:互动广告助力开发者高效流量变现

Flat Ads作为掌握7亿独家开发者流量的全球化营销推广平台,始终致力于为全球广告主提供高效、精准的效果营销和品牌宣传服务,同时为发布商提供高效变现、最大化收益的一站式解决方案。 Flat Ads旗下的互动广告平台作为高效能广告媒介,通过丰富的广告形式和变现场景有效地提升开…

【Ubuntu下 qmqtt6.2编译及使用】

这里写自定义目录标题 一、编译二、使用 背景&#xff1a;最近用QT编写简单的HMI软件&#xff0c;mqtt通信&#xff0c;记录下编译过程&#xff0c;供参考。 一、编译 QT6.5.3 qmqtt6.2&#xff08;源码地址&#xff1a;https://github.com/qt/qtmqtt/tree/6.5.3&#xff09; …

工具与技术:如何使用工具创建和实现导航栏图标动效

这篇教程的目的主要是带领大家做UI交互的入门引导,让大家理解做交互动效的一些基本逻辑思维,利用原型交互动画做导航栏icon动画效果。 导航栏icon动效的详细教程&#xff1a; 即时设计 - 可实时协作的专业 UI 设计工具即时设计是一款支持在线协作的专业级 UI 设计工具&#x…

Linux入门攻坚——26、Web Service基础知识与httpd配置-2

http协议 URL&#xff1a;Uniform Resource Locator&#xff0c;统一资源定位符 URL方案&#xff1a;scheme&#xff0c;如http://&#xff0c;https:// 服务器地址&#xff1a;IP&#xff1a;port 资源路径&#xff1a; 示例&#xff1a;http://www.test.com:80/bbs/…

【mysql 安装启动失败】 没有网下 libssl.so.10 not found 如何解决?

问题描述&#xff1a; libssl.so.10 > not found libcrypto.so.10 > not found [rootmysql tools]# ls -l /usr/sbin/mysqld -rwxr-xr-x. 1 root root 64290024 Sep 14 2022 /usr/sbin/mysqld [rootmysql tools]# ldd /usr/sbin/mysqldlinux-vdso.so.1 (0x00007fff97105…

网络流量 数据包length计算

MTUMSSIP header(20 bytes)tcp header(20 bytes) lengthMTUEthernet header(14bytes) 其中MSS为Maximum Segment Size&#xff0c;即最大报文段长度&#xff0c;其受MTU大小影响&#xff0c;这里的MTU指的是三层的&#xff0c;二层的MTU固定为1500&#xff0c;不能修改。 MT…

SFF2004A-ASEMI无人机专用SFF2004A

编辑&#xff1a;ll SFF2004A-ASEMI无人机专用SFF2004A 型号&#xff1a;SFF1006A 品牌&#xff1a;ASEMI 封装&#xff1a;ITO-220AC 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;20A 最大循环峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;400V 最…

一种快速设计PCB外壳的方法

设计PCB外壳比较好用的工具是SW但是有时候需要快速设计外壳的情况下使用立创EDA的外壳设计功能很好用&#xff0c;设计完成之后可以直接导出STL文件&#xff1a; 可以看到设计的外壳还是蛮精美的&#xff1a; 特别注意&#xff0c;设计外壳的时候要考虑如何把PCB放进壳子中&…

市值3万亿英伟达的崛起:技术、坚持与市场的力量,厚积薄发的经典案例

在科技领域&#xff0c;英伟达&#xff08;NVIDIA&#xff09;的故事无疑是一个厚积薄发的经典案例。作为一家专注于图形处理单元&#xff08;GPU&#xff09;的公司&#xff0c;英伟达用31年的时间证明了技术的价值、计算的价值和坚持的价值。本文将详细探讨英伟达如何从一家市…

第7周作业——单片机定时器与串口通信的学习与应用

一、蜂鸣器 &#xff08;一&#xff09;蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件&#xff0c;常用来产生设备的按键音、报警音等提示信号&#xff0c;按照驱动方式可以分为如下两种&#xff1a; 1、有源蜂鸣器&#xff1a;内部自带振荡源&#xff0c;将正负极接上…

自动化测试:Autorunner的使用

自动化测试&#xff1a;Autorunner的使用 一、实验目的 1、掌握自动化测试脚本的概念。 2、初步掌握Autorunner的使用 二、Autorunner的简单使用 autoRunner使用方法 新建项目 a) 在项目管理器空白区域,右键鼠标,选择新建项目 b) 输入项目名后,点击[确定]. 在初次打开aut…

[java]集合类stream的相关操作

1.对list中的map进行分组 下面例子中&#xff0c;根据高度height属性进行分组 List<Map<String, Float>>originalList new ArrayList<>();originalList.add(new HashMap<String,Float>() {{put("lng", 180.0f);put("lat",90f);…

Eclipse使用TFS(Team Foundation Server) 超详细

Eclipse使用TFS 1、什么是TFS2、TFS和Git的区别3、签出代码4、签入代码4.1、签出以进行编辑4.2、修改本地代码4.3、签入挂起的更改4.4、签入 如果不能 签入挂起的更改&#xff0c;则先 签出以进行编辑如果 签入挂起的更改不可选中&#xff0c;则 如下操作 1、什么是TFS Team F…

阿里云使用域名访问部署网站【2024 详细版】

目录 一、注册域名 1.创建信息模板 2.查询注册域名 二、域名设置 1.SSL证书 2.域名解析 3.宝塔设置 一、注册域名 1.创建信息模板 点击右上角【三】-【域名】-【信息模板】-【创建信息模板】- 填写信息 模板分为个人和企业两种&#xff0c;根据情况进行创建即可&…

c++ 里构造函数的形参与数据成员的同名问题

如题&#xff0c;这时&#xff0c;或许在 java 里&#xff0c;会报语法错误。但在 c vs2019 开了 c20语法规范。这不再是错误。这样的好处是解决了咱们的起变量名的麻烦&#xff1a;重名现在已不是错误&#xff0c;编译器可以解决了。测试如下&#xff1a; 我们看看 c 编译器是…