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.hqwc.cn/news/640172.html

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

相关文章

使用QQ邮箱进行登录验证

使用场景不多说&#xff0c;接下来直接看实现~ 登录到QQ邮箱&#xff0c;进入设置 打开IMAP/SMTP服务&#xff0c;记得把授权码记录下来&#xff0c;后面配置文件中需要用到 新建application的配置文件 spring:mail:# 指定邮件服务器地址host: smtp.qq.comusername: 你自己的q…

密钥密码学(二)

原文&#xff1a;annas-archive.org/md5/b5abcf9a07e32fc6f42b907f001224a1 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;可变长度分数化 本章涵盖 基于摩尔斯电码的密码 混合字母和双字母 可变长度二进制码字 基于文本压缩的密码 本章涵盖…

进阶C语言-文件操作

文件操作 &#x1f388;1.为什么使用文件&#x1f388;2.什么是文件&#x1f52d;2.1程序文件&#x1f52d;2.2数据文件&#x1f52d;2.3文件名 &#x1f388;3.文件的打开和关闭&#x1f52d;3.1文件指针&#x1f52d;3.2文件的打开和关闭 &#x1f388;1.为什么使用文件 ✅ 我…

Python基础03-深入探索Python字典操作

在Python中&#xff0c;字典是一种非常强大和灵活的数据结构&#xff0c;可以存储键值对&#xff0c;并提供了许多方法来操作这些键值对。本文将深入探讨Python字典的各种操作&#xff0c;包括如何创建、修改、合并和查找字典中的元素。 1. 创建字典 要创建一个字典&#xff…

创建虚拟环境(记录一下)

conda create -n name python3.8版本高于11.7&#xff1a; pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117 --force-reinstall --user 检验是否为true import torch print(torch.cuda.is_available()) stable diff…

4款值得推荐的AI辅助编程工具(支持C#语言)

前言 在这个AI迅速发展的阶段&#xff0c;涌现出了一大批好用的AI辅助编程工具。AI辅助编程工具能够提高开发效率、改善代码质量、降低bug率&#xff0c;是现代软件开发过程中的重要助手。今天大姚给大家分享4款AI辅助编程工具&#xff08;并且都支持C#语言&#xff09;&#…

AI重建粒子轨迹,发现新物理学

目录 二Sora冲击还没来&#xff0c;但智能家居人已经开始焦虑了&#xff01; 一、智能家居新革命&#xff1a;AIoH 二、AI技术接入智能家居&#xff0c;未来价值几何&#xff1f; 三、AI 智能家居&#xff0c;不是纸上谈兵 四、结语 电子学在核物理领域从来都不是一帆风顺…

nginx报shm_add_node::ngx_slab_alloc_locked()错误

问题&#xff1a; nginx 使用了 nginx-module-vts 做 nginx 监控&#xff0c;在 prometheus 和 grafana 中看不到相关的监控数据。在 nginx 的 error.log 日志中发现大量的 shm_add_node::ngx_slab_alloc_locked()错误信息&#xff0c;大概内容如下&#xff1a; 2024/04/16 1…

配置 rust国内源

rust crate.io 配置国内源&#xff08;cargo 国内源&#xff09; warning: spurious network error (2 tries remainin..._warning: spurious network error (3 tries remaining-CSDN博客

Nodejs安装与配置--基于Linux系统--RedHat7.9

nodejs安装从未这么简单 1、nodejs版本设置&#xff1f; curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash - 其他版本如下&#xff1a; * https://rpm.nodesource.com/setup_16.x — Node.js 16 "Gallium" (deprecated) * https://rpm.nodesource.co…

《HCIP-openEuler实验指导手册》1.3Apache动态功能模块加载卸载练习

1.3.1 配置思路 mod_status 模块可以帮助管理员通过web界面监控Apache运行状态&#xff0c;通过LoadModule指令加载该模块&#xff0c;再配置相关权限&#xff0c;并开启ExtendedStatus后&#xff0c;即可使用该模块。 1.3.2 配置步骤 检查mod_status模块状态&#xff08;使…

腾讯云服务器价格明细表2024年最新(CPU内存/带宽/磁盘)

腾讯云服务器价格明细表2024年最新&#xff08;CPU内存/带宽/磁盘&#xff09;腾讯云服务器租用优惠价格表&#xff1a;轻量应用服务器2核2G3M价格61元一年&#xff0c;2核2G4M价格99元一年、135元15个月、540元三年&#xff0c;2核4G5M带宽165元一年、252元15个月、756元3年&a…