Go语言并发控制

channel

// cancelFn 数据通道关闭通知退出
func cancelFn(dataChan chan int) {for {select {case val, ok := <-dataChan:// 关闭data通道时,通知退出// 一个可选是判断data=指定值时退出if !ok {fmt.Printf("Channel closed !!!")return}fmt.Printf("Receive data from dataChan %d\n", val)}}
}func main() {channels := make([]chan int, 10)for i := 0; i < 10; i++ {channels[i] = make(chan int)go cancelFn(channels[i])channels[i] <- 1 // 向管道写数据fmt.Println(i, "quit")}
}

watitGroup

var wg sync.WaitGroupfunc main() {ch := make(chan int)wg.Add(1) //设置计数器 表示goroutine个数加1go func() {v, ok := <-chif ok {fmt.Println("value", v)}wg.Done() //执行结束之后 , goroutine个数减1}()wg.Add(1)go func() {ch <- 4wg.Done()}()wg.Wait() //主goroutine阻塞,等待计数器变为0
}

WaitGroup原理

type WaitGroup struct {statel [3]uint32/*长度为3的数组包含两个计数器和一个信号量counter : 当前还未执行的结束的goroutine计数器waiter count : 等待goroutine-group结束的goroutine数量semaphore: 信号量*/
}

WaitGroup对外提供了三个接口

  • Add(delta int) : 将delta值加到counter中
  • Wait(): waiter递增加1 , 并阻塞等待信号量semaphore
  • Done(): counter递减1 , 按照waiter数值释放相应次数的信号量

Add(delta int)

Add() 做了两件事 , 一是把delta值累加到counter中,因为delta可以为负值.所以说当counter变为0时,根据waiter数值释放等量的信号量 , 把等待的goroutine全部唤醒,如果couner变为负值,则触发panic.

Wait()

Wait()方法一个是要累加waiter , 二是阻塞等待信号量.

Done()

Done 只做一件事,把counter减少1,其实Done里面调用的就是Add(-1)

context原理

Context实际上只定义了接口,凡是实现该接口的类都能称为Context.

type Context interface {Deadline() (deadline time.Time , ok bool)Done() <-chan struct{}Err() errorvalue(key interface{}) interface{}
}

Deadline()

该方法返回一个deadline和标识是否已设置deadline的bool值,如果没有设置deadline , 则ok为false,此时deadline为一个初始值的time.Time值.

Done()

该方法返回一个用于探测context是否取消的channel,当context取消时,会自动将该channel关闭. 对于不支持取消的context(如:context.Backgroud) , 该方法可能会返回nil.

Err()

该方法描述context关闭的原因.关闭原因由context实现控制.

value()

有一种context,它不是用于控制呈树状分布的goroutine , 而是用于在树状分布的goroutine之间传递信息.Value()方法就是此种类型的context,根据key查询map集合中的value.

空context

context包中定义了一个公用的emptyCtx全局变量 , 名为backgroud,可以使用context.Backgroud()获取它.context包中提供了四个方法创建不同类型的context , 使用这四个方法如果没有父context,则都需要传入background , 即将background作为父节点:

  • WithCancel();
  • WithDeadline();
  • WithTimeout();
  • WithValue();

context包中实现Context接口的struct,除了emptyCtx , 还有cancelCtx , timerCtx 和 valueCtx三种.

cancelCtx

type cancelCtx struct {Contextmu sync.Mutexdone chan struct{}children map[canceler]struct{}err error
}

children 中记录了由此context 派生的所有child , 此context被"cancel"时,会把其中所有的child都cancel掉.cancelCtx与deadline和value无关 , 所以只需要实现Done() 和 Err() 外露接口即可.

Cancel()接口的实现

cancel()内部方法时理解cancelCtx的关键cancelCtx.children的map中,其中key值即后代对象,value值并没有意义.

func (c *cancelCtx) cancel(removeFromParent bool, err error) {c.mu.Lock()c.err = err                       //设置一个error,说明关闭原因close(c.done)                     //将channel关闭,以此通知派生的contextfor child := range c.children {   //遍历所有children,逐个调用cancel方法child.cancel(false, err)}c.children = nilc.mu.Unlock()if removeFromParent {            //正常情况下,需要将自己从parent删除removeChild(c.Context, c)}
}

WithCancel()方法的使用案例

func HandelRequest(ctx context.Context) {go WriteRedis(ctx)go WriteDatabase(ctx)for {select {case <-ctx.Done():fmt.Println("HandelRequest Done.")returndefault:fmt.Println("HandelRequest running")time.Sleep(2 * time.Second)}}
}
func WriteRedis(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteRedis Done.")returndefault:fmt.Println("WriteRedis running")time.Sleep(2 * time.Second)}}
}
func WriteDatabase(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteDatabase Done.")returndefault:fmt.Println("WriteDatabase running")time.Sleep(2 * time.Second)}}
}
func main() {ctx, cancel := context.WithCancel(context.Background())go HandelRequest(ctx)time.Sleep(5 * time.Second)fmt.Println("It's time to stop all sub goroutines!")cancel()//Just for test whether sub goroutines exit or nottime.Sleep(5 * time.Second)
}

HandelRequest()用于处理某个请求 , 其又会创建两个协程 , main协程可以在适当时机cancel掉所有自子协程

timeCtx

type timerCtx struct {cancelCtxtimer *time.Timer deadline time.Time
}

timerCtx 在cancelCtx的基础上,增加了deadline用于标示自动cancel的最终时间,而timer就是一个触发自动cancel的定时器.由此衍生出了WithDeadline()和WithTimeout().

  • deadline:指定最后期限.
  • timeout: 指定最长存活时间.
package mainimport ("fmt""time""context"
)func HandelRequest(ctx context.Context) {go WriteRedis(ctx)go WriteDatabase(ctx)for {select {case <-ctx.Done():fmt.Println("HandelRequest Done.")returndefault:fmt.Println("HandelRequest running")time.Sleep(2 * time.Second)}}
}func WriteRedis(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteRedis Done.")returndefault:fmt.Println("WriteRedis running")time.Sleep(2 * time.Second)}}
}func WriteDatabase(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("WriteDatabase Done.")returndefault:fmt.Println("WriteDatabase running")time.Sleep(2 * time.Second)}}
}func main() {ctx, _ := context.WithTimeout(context.Background(), 5 * time.Second)go HandelRequest(ctx)time.Sleep(10 * time.Second)
}

valueCtx

type valueCtx struct {Contextkey, val interface{}
}

valueCtx 只是在Context基础上增加了一个key-value对,用于在各级协程之间传递数据.因此只需要实现Value()接口.

func HandelRequest(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("HandelRequest Done.")returndefault:fmt.Println("HandelRequest running, parameter: ", ctx.Value("parameter"))time.Sleep(2 * time.Second)}}
}func main() {ctx := context.WithValue(context.Background(), "parameter", "1")go HandelRequest(ctx)time.Sleep(10 * time.Second)
}

子协程可以读到context的key-value

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

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

相关文章

5. Django 探究CBV视图

5. 探究CBV视图 Web开发是一项无聊而且单调的工作, 特别是在视图功能编写方面更为显著. 为了减少这种痛苦, Django植入了视图类这一功能, 该功能封装了视图开发常用的代码, 无须编写大量代码即可快速完成数据视图的开发, 这种以类的形式实现响应与请求处理称为CBV(Class Base…

使用阿里云试用Elasticsearch学习:sentence-transformers 包使用

环境&#xff1a;centos8&#xff0c;windows坑太多。 一、检查linux环境openssl哪个版本&#xff08;如果是OpenSSL 1.1.1k 直接跳过&#xff09; [roothecs-334217 python39]# openssl version OpenSSL 1.0.2k-fips 26 Jan 2017原因后续会出麻烦&#xff0c;遇到这种情况最…

16.C++常用的算法_算数生成算法

文章目录 遍历算法1. accumulate()代码工程运行结果 2. fill()代码工程运行结果 遍历算法 1. accumulate() 代码工程 第三个参数为累加的起始值&#xff0c;如果没有特殊需求写0即可; 需要注意包含头文件#include<numeric>#define _CRT_SECURE_NO_WARNINGS #include&l…

【C语言】Dijkstra算法详解

一、引言二、Dijkstra算法原理三、Dijkstra算法的C语言实现四、Dijkstra算法的应用场景五、总结 一、引言 Dijkstra算法是一种著名的图论算法&#xff0c;用于解决单源最短路径问题。它是由荷兰计算机科学家Edsger W. Dijkstra在1956年提出的。本文将详细介绍Dijkstra算法的原理…

代码随想录:二叉树11-12

目录 222.完全二叉树的节点个数 题目 代码&#xff08;层序迭代&#xff09; 代码&#xff08;后序递归&#xff09; 代码&#xff08;满二次树递归&#xff09; 总结 110.平衡二叉树 题目 代码&#xff08;后序递归&#xff09; 代码&#xff08;层序迭代&#xff0…

短视频批量采集提取软件|视频关键词下载工具

短视频批量采集软件&#xff1a;快速抓取、高效下载 一、开发背景 随着短视频平台的兴起&#xff0c;获取并分析相关视频内容已成为许多业务的必要步骤。然而&#xff0c;传统的手动方式无法满足快速、批量获取的需求&#xff0c;因此我们开发了一款专业的短视频批量采集软件。…

Linux内核之读、写信号量:up_read、up_write用法实例(五十三)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

ASP.NET基于WEB的工作计划流程管理系统的设计与实现

摘 要 信息技术的飞速发展&#xff0c;尤其是网络通讯技术、数据库技术及自动化技术的日新月异&#xff0c;为单位、企业的办公带来了极大的便利。但是由于单位、企业的工作性质众多&#xff0c;工作流程各有差异&#xff0c;企业、单位、部门之间的管理机制各不相同&#xf…

用队列实现栈(力扣第225题)

#include "stdio.h" #include "stdbool.h" #include "string.h" #include "stdlib.h" #include "assert.h"//初始化队列 typedef int QueueDataType;typedef struct queue {QueueDataType val;struct queue* next; }Qnode;t…

微信小程序地图polyline坐标太多异常显示BUG

描述 微信小程序map地图上显示polyline线&#xff0c;点位超过1250个出现bug&#xff0c;&#xff08;仅真机上出现&#xff0c;模拟器上正常&#xff09; 这里以加载四川省边界为例, 以下是示例代码 // 读取geojson数据 uni.request({url: https://geo.datav.aliyun.com/a…

openplc Linux 使用modbus RTU 从机通讯

1.Linux 环境下&#xff0c;openplc 默认使用的是modbus tcp协议通信。 想要使用串口 modbus rtu 通讯可以通过在runtime中添加SlaveDevices从机设备 2.添加设备&#xff0c;分配地址。 左边添加串口配置&#xff0c;右边是需要通讯的地址&#xff0c;从机地址都是从100开始&am…

yolov8 区域计数

yolov8 区域计数 1. 基础2. 计数功能2.1 计数模块2.2 判断模块 3. 主代码4. 实验结果5. 源码 1. 基础 本项目是在 WindowsYOLOV8环境配置 的基础上实现的&#xff0c;测距原理可见上边文章 2. 计数功能 2.1 计数模块 在指定区域内计数模块 def count_objects_in_region(bo…

STM32 堆栈内存以及变量存储分布

STM32的程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内, 地址范围为0x0000 0000至0xFFFF FFFF。其中FLASH为ROM类型&#xff0c;储存的数据掉电不易失&#xff1b;RAM中存储的数据掉电易失。以STM32F103系列为例&#xff0c;最多有512KB的FLA…

Discuz! X3.4 升级至 Discuz! X3.5 详细教程

第一步&#xff1a;从其他以前的 Discuz! X 版本升级Discuz! X3.4 请先升级到Discuz! X3.4&#xff0c;升级教程网上比较普遍&#xff0c;在此不再论述。 第二步&#xff1a;Discuz! X3.4 升级至 Discuz! X3.5 &#xff08;Discuz 从 X3.5 以后&#xff0c;不在发布GBK版本&…

【软考】UML中的图之类图

目录 1. 说明2. 图示3. 类图使用方式3.1 对系统的词汇建模3.2 对简单的协作建模3.3 对逻辑数据库模式建模 1. 说明 1.类图&#xff08;Class Diagram&#xff09;展现了一组对象、接口、协作和它们之间的关系。2.在面向对象系统的建模中所建立的最常见的图是类图。3.类图给出系…

离线数仓数据导出-hive数据同步到mysql

离线数仓数据导出-hive数据同步到mysql MySQL建库建表数据导出 为方便报表应用使用数据&#xff0c;需将ads各指标的统计结果导出到MySQL数据库中。 datax支持hive同步MySQL&#xff1a;仅仅支持hive存储的hdfs文件导出。所以reader选hdfs-reader&#xff0c;writer选mysql-wri…

怎样在外网登录访问CRM管理系统?

一、什么是CRM管理系统&#xff1f; Customer Relationship Management&#xff0c;简称CRM&#xff0c;指客户关系管理&#xff0c;是企业利用信息互联网技术&#xff0c;协调企业、顾客和服务上的交互&#xff0c;提升管理服务。为了企业信息安全以及使用方便&#xff0c;企业…

SSM小程序作品集展示微信小程序

采用技术 SSM小程序作品集展示微信小程序的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 用户功能 用户注册 用户首页 作品集 优秀作者 我的分享 管…

powershell@命令行提示符样式配置自定义@pwsh重写prompt显示电量内存时间等信息

文章目录 abstract流行的powershell prompt模块示例 powershell原生修改Prompt函数配置文档Prompt命令来自哪里 简单修改带上电量和时间的Prompt 复杂修改预览FAQ:没有必要修改相关仓库地址样式选择平衡样式花哨样式响应性能 小结 abstract 在 PowerShell 中&#xff0c;可以通…

CSDN积分和等级和 能创建专栏数量的关系。还差1000多分!

积分查询&#xff1a;CSDN 博客积分规则 博客积分是CSDN对用户努力的认可和奖励&#xff0c;也是衡量博客水平的重要标准。博客等级也将由博客积分唯一决定。积分规则具体如下&#xff1a; 1、每发布一篇原创或者翻译文章&#xff1a;可获得10分&#xff1b; 2、每发布一篇转载…