Go语言并发控制

news2024/12/22 19:13:29

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.WaitGroup

func main() {
	ch := make(chan int)
	wg.Add(1) //设置计数器 表示goroutine个数加1
	go func() {
		v, ok := <-ch
		if ok {
			fmt.Println("value", v)
		}
		wg.Done() //执行结束之后 , goroutine个数减1
	}()
	wg.Add(1)
	go func() {
		ch <- 4
		wg.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() error
    value(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 {
    Context
    mu sync.Mutex
    done 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关闭,以此通知派生的context

    for child := range c.children {   //遍历所有children,逐个调用cancel方法
        child.cancel(false, err)
    }
    c.children = nil
    c.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.")
			return
		default:
			fmt.Println("HandelRequest running")
			time.Sleep(2 * time.Second)
		}
	}
}
func WriteRedis(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteRedis Done.")
			return
		default:
			fmt.Println("WriteRedis running")
			time.Sleep(2 * time.Second)
		}
	}
}
func WriteDatabase(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("WriteDatabase Done.")
			return
		default:
			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 not
	time.Sleep(5 * time.Second)
}

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

timeCtx

type timerCtx struct {
    cancelCtx
    timer *time.Timer 
    deadline time.Time
}

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

  • deadline:指定最后期限.
  • timeout: 指定最长存活时间.
package main

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

func HandelRequest(ctx context.Context) {
    go WriteRedis(ctx)
    go WriteDatabase(ctx)
    for {
        select {
        case <-ctx.Done():
            fmt.Println("HandelRequest Done.")
            return
        default:
            fmt.Println("HandelRequest running")
            time.Sleep(2 * time.Second)
        }
    }
}

func WriteRedis(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("WriteRedis Done.")
            return
        default:
            fmt.Println("WriteRedis running")
            time.Sleep(2 * time.Second)
        }
    }
}

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

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

func HandelRequest(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("HandelRequest Done.")
            return
        default:
            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.coloradmin.cn/o/1617585.html

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

相关文章

前端页面助手 (vue)

快速开发页面&#xff08;图形化开发页面&#xff09; 自主编辑 然后自己也可以修改属性 最后导出页面即可 github地址 ;https://github.com/opentiny/tiny-engine

蚓链数字化营销系统与数字资产的关系

蚓链数字化营销系统是一种利用数字技术来实现营销目标的系统。它集成了多种数字营销工具和渠道&#xff0c;以收集、分析和利用客户数据&#xff0c;优化营销活动&#xff0c;并提高营销效果。 数字资产是一种新型的资产类别&#xff0c;它们以电子数据的形式存在&#xff0c;可…

前端开发攻略---实现发送手机验证码60s倒计时效果(手机号验证+按钮文字自定义显示+Vue2写法+Vue3写法)

1、演示 2、说明 1、为了便于演示&#xff0c;本示例将在3秒后就再次发送。您可以根据需要自定义此时间间隔。 2、采用最少的变量以满足需求&#xff0c;以减少内存占用。 3、不仅仅局限于按钮情况&#xff0c;也可应用于不禁用按钮的情况&#xff0c;以实现更多的扩展性。 4、…

02 VMware下载安装银河麒麟(Kylin)系统

02 VMware下载&安装银河麒麟&#xff08;Kylin&#xff09;系统 一、官网1、官网地址 二、下载1、官网下载&#xff08;1&#xff09;服务器操作系统&#xff08;2&#xff09;申请试用&#xff08;3&#xff09;产品试用申请&#xff08;4&#xff09;点击下载连接即可 2、…

Redis篇:缓存穿透以及解决方案

1.何为缓存穿透 缓存穿透 &#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 比如查询一个id 0的数据&#xff0c;这是在redis和数据库中肯定不存在的&#xff0c;这样就属于缓存穿…

vue2+vxe-table实现表格增删改查+虚拟滚动

vue2vxe-table实现表格增删改查虚拟滚动 使用的vxe-table版本&#xff1a;v3.x (vue 2.6 长期维护版) 完整代码 <template><div><vxe-toolbar ref"xToolbar" export :refresh"{query: findList}"><template #buttons><vxe-b…

【网络安全】在网络中如何对报文和发送实体进行鉴别?

目录 1、报文鉴别 &#xff08;1&#xff09;使用数字签名进行鉴别 &#xff08;2&#xff09;密码散列函数 &#xff08;3&#xff09;报文鉴别码 2、实体鉴别 鉴别(authentication) 是网络安全中一个很重要的问题。 一是要鉴别发信者&#xff0c;即验证通信的对方的确是…

富唯智能:打造未来机器人教育新标杆

随着科技的飞速发展&#xff0c;机器人教育正逐渐成为培养未来人才的重要领域。富唯智能&#xff0c;作为业内领先的机器人技术提供商&#xff0c;近日推出了一款全新的机器人教育实践平台系统&#xff0c;旨在为学生提供更加丰富、更具挑战性的学习体验。 该平台系统以AUBO-i5…

A Neural Span-Based Continual Named Entity Recognition Model

《A Neural Span-Based Continual Named Entity Recognition Model》------------AAAI’23 论文链接&#xff1a;https://arxiv.org/abs/2302.12200 代码&#xff1a;https://github.com/Qznan/SpanKL 当前问题&#xff1a; 1.现有的NER模型在适应新的实体类型时往往表现不佳…

基于CAPL的S19文件解析

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

js的算法-交换排序(快速排序)

快速排序 基本思想 快速排序的基本思想是基于分治法的&#xff1a;在待排序表L【1...n】中任意取一个元素p 作为枢轴&#xff08;或基准&#xff0c;通常取首元素&#xff09;。通过一趟排序将待排序表划分为独立的两部分L【1...k-1】和L【k1...n】;这样的话&#xff0c;L【1…

低代码技术与仓储管理的新纪元:革命性的供应链变革

引言 在当今数字化时代&#xff0c;企业对于创新和效率的追求越发迫切。在这样的背景下&#xff0c;低代码技术应运而生&#xff0c;成为企业数字化转型的重要工具之一。低代码技术的崛起为企业提供了一种快速、灵活、成本效益高的开发方式&#xff0c;大大缩短了软件开发周期…

如何把视频压缩变小?你应该知道的三个压缩视频的方法

一&#xff0c;视频压缩的基本原理 视频压缩的基本原理是通过去除视频中的冗余信息&#xff0c;减少视频数据的大小&#xff0c;从而达到压缩的目的。视频压缩的方法有很多种&#xff0c;其中最常见的是有损压缩和无损压缩。 二&#xff0c;视频压缩的必要性 1&#xff0c;视…

【智能算法】回溯搜索算法(BSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2013年&#xff0c;P Civicioglu等人受到当前种群与历史种群之间的差分向量的引导启发&#xff0c;提出了回溯搜索算法&#xff08;Backtracking Search Algorithm, BSA&#xff09;。 2.算法原理…

甄美天使1+3退休模式开发|最新升级模式

我是新零售商业架构师肖琳&#xff0c;易创客社交新零售创始‌‌‌‌‌人&#xff0c;擅长品牌从0-1、1-10起盘全过程。易创客新零售&#xff0c;提供商业模式解决方案系统&#xff0c;包括分销系统、微商系统、新零售系统&#xff1b;提供社交新零售陪跑代运营&#xff0c;提供…

Mybatis 一级缓存和二级缓存

文章目录 前言查询缓存一级缓存应用场景生效的条件测试一级缓存原理工作流程源码分析 一级缓存总结 二级缓存二级缓存配置源码分析 为什么 MyBatis 默认不开启二级缓存&#xff1f; 前言 MyBatis是常见的Java数据库访问层框架。在日常工作中&#xff0c;开发人员多数情况下是使…

宁盾LDAP统一用户认证与单点登录:构建高效安全的企业身份认证

在信息化时代&#xff0c;企业面临着众多的应用系统和数据资源&#xff0c;如何有效地管理和保护这些资源&#xff0c;确保信息安全和高效利用&#xff0c;成为了企业信息化建设的核心问题。LDAP统一用户认证和单点登录&#xff08;SSO&#xff09;作为一种高效、安全的身份验证…

学习笔记记录ensp中防火墙配置(trust,unstrus,dmz 资源下载可用)

实验目的&#xff0c;通过配置防火墙控制相互之间的访问&#xff0c;拓扑图如下 资源已上传&#xff0c;注意lsw1和ar2的路由表到各个网段的路由表配置&#xff0c;通过防火墙来控制各个区域能否访问成功。 防火墙通过cloud2链接&#xff0c;方便登录网页配置防火墙策略。防火…

构建智慧银行:现代化系统架构的探索与实践

在数字化时代&#xff0c;银行业正面临着前所未有的挑战和机遇。随着科技的不断发展&#xff0c;传统银行业务已经难以满足客户日益增长的需求&#xff0c;智慧银行系统的建设成为了行业的必然选择。本文将探讨智慧银行系统的架构设计与实践&#xff0c;以期为银行业的数字化转…