当前位置: 首页 > news >正文

百度面试手撕 go context channel部分学习

题目

手撕 对无序的切片查询指定数 使用context进行子协程的销毁 并且进行超时处理。

全局变量定义

var (startLoc = int64(0) // --- 未处理切片数据起始位置endLoc = int64(0) // --- 切片数据右边界 避免越界offset   = int64(0) // --- 根据切片和协程数量 在主线程 动态设置target   = 42 // --- 设置的目标值mu       sync.Mutex // --- 避免并发冲突使用的全局锁
)

1.并发处理

1.1 使用atomic原子操作

使用CAS操作解决并发问题(不使用锁) 效率上和使用全局锁在 100000 上几乎没差别

// --- 使用atomic原子操作
start = atomic.LoadInt64(&startLoc)
end = start + offset
if end > endLoc {end = endLoc
}
// 应该不会出现ABA问题
if ok := atomic.CompareAndSwapInt64(&startLoc, start, end); ok == false {continue
}

1.2 使用全局锁

mu.Lock()
start = startLoc
end = start + offset
startLoc = end
mu.Unlock()
if start >= endLoc {return
}
if end > endLoc {end = endLoc
}

1.3主线程手动切片全部代码

package mainimport ("context""fmt""sync""sync/atomic""time"
)func find(nums []int, ctx context.Context, wg *sync.WaitGroup, target int, start, end int64) {defer wg.Done()for {select {case <-ctx.Done():// 如果接收到取消信号,退出协程returndefault:for i := start; i < end; i++ {if nums[i] == target {// 使用 atomic 以确保线程安全atomic.StoreInt32(&valid, 1)return}}return}}
}var valid int32func main() {sliceLen := int64(1000000)// 创建一个背景上下文和一个取消功能ctx := context.Background()// 假设 ddl 是一个固定的截止时间ddl := time.Now().Add(10 * time.Second) // 假设 5 秒钟后超时newCtx, cancel := context.WithDeadline(ctx, ddl)// 创建一个较大的切片 nums 并初始化nums := make([]int, sliceLen)// 初始化切片为随机数据,例如从 1 到 100,值为42的即为目标for i := 0; i < len(nums); i++ {nums[i] = i}offset := sliceLen / 10startLoc := int64(0)startTime := time.Now()// 使用 WaitGroup 来等待所有协程完成var wg sync.WaitGroup// 启动多个协程进行查找for i := 0; i < 10; i++ {wg.Add(1)go find(nums, newCtx, &wg, 42, startLoc, startLoc+offset)startLoc = startLoc + offset}// 等待结果go func() {wg.Wait()cancel() // 等待所有协程结束后,调用 cancel}()// 检查结果select {case <-newCtx.Done():if atomic.LoadInt32(&valid) == 1 {fmt.Println("Found target!")} else {fmt.Println("Timeout or not found.")}}duration := time.Since(startTime)fmt.Printf("程序运行时间: %s\n", duration)
}

1.4 采取锁处理 & 原子操作 全部代码

package mainimport ("context""fmt""sync""sync/atomic""time"
)var (startLoc = int64(0)endLoc   = int64(0)offset   = int64(0)target   = 42mu       sync.Mutex
)func find(nums []int, ctx context.Context, wg *sync.WaitGroup) {defer wg.Done()var start, end int64for {select {case <-ctx.Done():// 如果接收到取消信号,退出协程returndefault:// --- 使用全局锁// 查找区间//mu.Lock()//start = startLoc//end = start + offset//startLoc = end//mu.Unlock()//if start >= endLoc {//	return//}//if end > endLoc {//	end = endLoc//}// --- 使用atomic原子操作start = atomic.LoadInt64(&startLoc)end = start + offsetif end > endLoc {end = endLoc}if start >= endLoc {return}// 应该不会出现ABA问题if ok := atomic.CompareAndSwapInt64(&startLoc, start, end); ok == false {//time.Sleep(100)continue}for i := start; i < end; i++ {if nums[i] == target {// 使用 atomic 以确保线程安全atomic.StoreInt32(&valid, 1)return}}}}
}var valid int32func main() {sliceLen := int64(100000)// 创建一个背景上下文和一个取消功能ctx := context.Background()// 假设 ddl 是一个固定的截止时间ddl := time.Now().Add(10 * time.Second) // 假设 5 秒钟后超时newCtx, cancel := context.WithDeadline(ctx, ddl)// 创建一个较大的切片 nums 并初始化nums := make([]int, sliceLen)endLoc = sliceLen// 初始化切片为随机数据,例如从 1 到 100,值为42的即为目标for i := 0; i < len(nums); i++ {nums[i] = i}startTime := time.Now()// 使用 WaitGroup 来等待所有协程完成var wg sync.WaitGroupoffset = int64(sliceLen / 10)// 启动多个协程进行查找for i := 0; i < 10; i++ {wg.Add(1)go find(nums, newCtx, &wg)}// 等待结果go func() {wg.Wait()cancel() // 等待所有协程结束后,调用 cancel}()// 检查结果select {case <-newCtx.Done():if atomic.LoadInt32(&valid) == 1 {fmt.Println("Found target!")} else {fmt.Println("Timeout or not found.")}}duration := time.Since(startTime)fmt.Printf("程序运行时间: %s\n", duration)
}

 2.Context部分

2.1 context是并发安全

创建的初始context有两种 TODO()和Background(),查看内部结构体, 实际都是emptyCtx。

Background()创建的上下文通常被认为整个请求的顶级 Context,而TODO()创建的通常被认为是暂时的、未确定的 Context。

func Background() Context {return backgroundCtx{}
}func TODO() Context {return todoCtx{}
}
1. 传值Value

直接对父context进行包装,并不会修改父context

type valueCtx struct {Contextkey, val any
}func WithValue(parent Context, key, val any) Context {if parent == nil {panic("cannot create context from nil parent")}if key == nil {panic("nil key")}if !reflectlite.TypeOf(key).Comparable() {panic("key is not comparable")}return &valueCtx{parent, key, val}
}
2. 设置超时时间 WithDeadline
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {return WithDeadlineCause(parent, d, nil)
}

2.2 context的信号传递

以cancel部分举例说明

1. 设置超时时间

设置取消函数的接口主要分为下列几种情况:

  1. 父Ctx为nil, 抛出异常
  2. 父Ctx具有超时时间,且比设置的超时时间更早结束,则新建CancelCtx加入父Ctx监听列表,且返回该新建CancelCtx。
  3. 设置新的包含超时时间的timerCtx(内部继承了cancelCtx结构体),加入父Ctx的监听列表,检查是否已经超时, 超时则取消该上下文, 没超时则设置计时器,等待取消。
func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc) {if parent == nil {panic("cannot create context from nil parent")}if cur, ok := parent.Deadline(); ok && cur.Before(d) {// The current deadline is already sooner than the new one.return WithCancel(parent)}c := &timerCtx{deadline: d,}c.cancelCtx.propagateCancel(parent, c)dur := time.Until(d)if dur <= 0 {c.cancel(true, DeadlineExceeded, cause) // deadline has already passedreturn c, func() { c.cancel(false, Canceled, nil) }}c.mu.Lock()defer c.mu.Unlock()if c.err == nil {c.timer = time.AfterFunc(dur, func() {c.cancel(true, DeadlineExceeded, cause)})}return c, func() { c.cancel(true, Canceled, nil) }
}
2.设置子Ctx监听父Ctx

上下文取消传播:propagateCancel 的核心目的是将父上下文的取消信号(及其取消原因)传递给子上下文。不同的父上下文类型(如 *cancelCtx 或实现了 AfterFunc 方法的上下文)会采取不同的处理方式。
并发处理:通过 goroutines.Add(1) 和新的 goroutine 来监听父上下文的取消事件,确保并发场景下的取消传播。

其中分为三种情况:

  1. 父Ctx未设置Done ,则无需监听
  2. 父Ctx设置了回调函数
  3. 父Ctx类型是*cancelCtx,则把子Ctx加入自身map中,每个子Ctx都会开启协程监听父Ctx信号,同步取消自身。

主要就是依赖Channel进行信号传递

func (c *cancelCtx) propagateCancel(parent Context, child canceler) {c.Context = parentdone := parent.Done()if done == nil {return // parent is never canceled}select {case <-done:// parent is already canceledchild.cancel(false, parent.Err(), Cause(parent))returndefault:}if p, ok := parentCancelCtx(parent); ok {// parent is a *cancelCtx, or derives from one.p.mu.Lock()if p.err != nil {// parent has already been canceledchild.cancel(false, p.err, p.cause)} else {if p.children == nil {p.children = make(map[canceler]struct{})}p.children[child] = struct{}{}}p.mu.Unlock()return}if a, ok := parent.(afterFuncer); ok {// parent implements an AfterFunc method.c.mu.Lock()stop := a.AfterFunc(func() {child.cancel(false, parent.Err(), Cause(parent))})c.Context = stopCtx{Context: parent,stop:    stop,}c.mu.Unlock()return}goroutines.Add(1)go func() {select {case <-parent.Done():child.cancel(false, parent.Err(), Cause(parent))case <-child.Done():}}()
}

参考链接:

Go 语言并发编程与 Context | Go 语言设计与实现

3.channel部分

3.1channel底层结构

在有缓冲区的channel部分,数据使用环形链表进行存储,存储有变量记录有效数据区域。

type hchan struct {qcount   uint           // Channel 中的元素个数dataqsiz uint           // Channel 中的循环队列的长度buf      unsafe.Pointer // Channel 的缓冲区数据指针elemsize uint16closed   uint32elemtype *_type // element typesendx    uint   // Channel 的发送操作处理到的位置recvx    uint   // Channel 的接收操作处理到的位置recvq    waitq  // 等待消息的双向链表sendq    waitq  // 发生消息双向链表// lock protects all fields in hchan, as well as several// fields in sudogs blocked on this channel.// Do not change another G's status while holding this lock// (in particular, do not ready a G), as this can deadlock// with stack shrinking.lock mutex
}// 创建双向链表 构造等待消息 或 发生消息的goroutine的双向链表
type waitq struct {first *sudog  last  *sudog
}

有缓冲区

无缓冲区

3.2 对于不同的channel进行读入读出的不同情况

如果给一个 nil 的 channel 发送数据,会造成永远阻塞。

如果从一个 nil 的 channel 中接收数据,也会造成永久阻塞。

给一个已经关闭的 channel 发送数据, 会引起 panic。

从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值。

同时分为有缓冲区和无缓冲区两种,前者是异步的,在缓冲区未满时,可以持续输入,不会阻塞,直到缓冲区满;后者则为有goroutine输入,等待有协程进行数据消费,否则持续阻塞。

对nil的channel不可操作。

参考链接:

https://www.cnblogs.com/Paul-watermelon/articles/17484439.html

Go 语言 Channel 实现原理精要 | Go 语言设计与实现 (draveness.me)

相关文章:

百度面试手撕 go context channel部分学习

题目 手撕 对无序的切片查询指定数 使用context进行子协程的销毁 并且进行超时处理。 全局变量定义 var (startLoc int64(0) // --- 未处理切片数据起始位置endLoc int64(0) // --- 切片数据右边界 避免越界offset int64(0) // --- 根据切片和协程数量 在主线程 动态设…...

Spring事务管理详解

一、什么是事务管理 事务是一个最小的不可再分的工作单元。 一个事务对应一套完整的业务操作。事务管理是指这些操作要么全部成功执行&#xff0c;要么全部回滚&#xff0c;从而保证数据的一致性和完整性。比如银行转账&#xff0c;需要保证转出和转入是一个原子操作。Spring提…...

社区版 IDEA 开发webapp 配置tomcat

1.安装tomcat 参考Tomcat配置_tomcat怎么配置成功-CSDN博客 2.构建webapp项目结构 新建一个普通项目 然后添加webapp的目录结构&#xff1a; main目录下新建 webapp 文件夹 webapp文件夹下新建WEB_INF文件夹 *WEB_INF目录下新建web.xml wenapp文件夹下再新建index.html …...

打 印 菱 形

本题要求你写个程序打印成菱形的形状。例如给定17个符号&#xff0c;要求按下列格式打印 **** *********所谓“菱形形状”&#xff0c;是指每行输出奇数个符号&#xff1b;各行符号中心对齐&#xff1b;相邻两行符号数差2&#xff1b;符号数从1开始先从小到大顺序递增&#xff…...

ffmpeg翻页转场动效的安装及使用

文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目 三、安装3.1、安装依赖&#xff08;[参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew)&#xff09;3.2、获取…...

[RocketMQ] 发送重试机制与消费重试机制~

发送重试 RocketMQ 客户端发送消息时&#xff0c;由于网络故障等因素导致消息发送失败&#xff0c;这时客户端SDK会触发重试机制&#xff0c;尝试重新发送以达到调用成功的效果。 触发条件 客户端消息发送请求失败或超时。服务端节点处于重启或下线状态。服务端运行慢造成请…...

基于Redis的网关鉴权方案与性能优化

文章目录 前言一、微服务鉴权1.1 前端权限检查1.2 后端权限检查1.3 优缺点 二、网关鉴权2.1 接口权限存储至Redis2.2 网关鉴权做匹配 总结 前言 在微服务架构中&#xff0c;如何通过网关鉴权结合Redis缓存提升权限控制的效率与性能。首先&#xff0c;文章对比了两种常见的权限…...

计算机网络-L2TP VPN基础概念与原理

一、概述 前面学习了GRE和IPSec VPN&#xff0c;今天继续学习另外一个也很常见的VPN类型-L2TP VPN。 L2TP&#xff08;Layer 2 Tunneling Protocol&#xff09; 协议结合了L2F协议和PPTP协议的优点&#xff0c;是IETF有关二层隧道协议的工业标准。L2TP是虚拟私有拨号网VPDN&…...

Node.js day-01

01.Node.js 讲解 什么是 Node.js&#xff0c;有什么用&#xff0c;为何能独立执行 JS 代码&#xff0c;演示安装和执行 JS 文件内代码 Node.js 是一个独立的 JavaScript 运行环境&#xff0c;能独立执行 JS 代码&#xff0c;因为这个特点&#xff0c;它可以用来编写服务器后端…...

vue el-dialog实现可拖拉

el-dialog实现拖拉&#xff0c;每次点击度居中显示&#xff0c;以下贴出代码具体实现&#xff0c;我是可以正常拖拉并且每次度显示在中间&#xff0c;效果还可以&#xff0c;需要的可以丢上去跑跑 组件部分&#xff1a; <el-dialog:visible.sync"dialogVisible"…...

go配置文件

https://github.com/spf13/viper viper golang中常用的配置文件工具为viper库&#xff0c;是一个第三方库。viper功能&#xff1a; 解析JSON、TOML、YAML、HCL等格式的配置文件。监听配置文件的变化(WatchConfig)&#xff0c;不需要重启程序就可以读到最新的值。...

C++ OpenGL学习笔记(2、绘制橙色三角形绘制、绿色随时间变化的三角形绘制)

相关文章链接 C OpenGL学习笔记&#xff08;1、Hello World空窗口程序&#xff09; 目录 绘制橙色三角形绘制1、主要修改内容有&#xff1a;1.1、在主程序的基础上增加如下3个函数1.2、另外在主程序外面新增3个全局变量1.3、编写两个shader程序文件 2、initModel()函数3、initS…...

项目搭建+删除(单/批)

一 : 删除没有单独的页面,在列表页面写 二 : 删除在列表的页面 1.删除(单/双)的按钮 ① : 在列表文档就绪函数的ajax里面,成功回调函数追加数据里写删除按钮 注意点 : 删除/修改/回显都是根据id来的,记得传id ② : 批删给批删按钮,定义批删的方法 one : 示例(单删) : //循环追…...

《小米创业思考》

《小米创业思考》是小米创始人雷军对小米创业历程的系统梳理和深度思考&#xff0c;蕴含着许多宝贵的创业经验与智慧&#xff0c;以下是主要内容&#xff1a; 创业初心与梦想 - 源于热爱与使命感&#xff1a;雷军及团队怀着对科技的热爱和让每个人享受科技乐趣的使命感创立小米…...

多种注意力机制详解及其源码

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

VMWare 的克隆操作

零、碎碎念 VMWare 的这个克隆操作很简单&#xff0c;单拎出来成贴的目的是方便后续使用。 一、操作步骤 1.1、在“源”服务器上点右键&#xff0c;选择“管理--克隆” 1.2、选择“虚拟机的当前状态”为基础制作克隆&#xff0c;如下图所示&#xff0c;然后点击“下一页” 1.3、…...

Y3编辑器教程7:界面编辑器

文章目录 一、简介1.1 导航栏1.2 画板1.3 场景界面1.4 控件1.4.1 空节点1.4.2 按钮1.4.3 图片1.4.4 模型1.4.5 文本1.4.6 输入框1.4.7 进度条1.4.8 列表 1.5 元件1.5.1 简介1.5.2 差异说明1.5.3 元件实例的覆盖、还原与禁止操作1.5.4 迷雾控件 1.6 属性1.7 事件&#xff08;动画…...

「Mac畅玩鸿蒙与硬件45」UI互动应用篇22 - 评分统计工具

本篇将带你实现一个评分统计工具&#xff0c;用户可以对多个选项进行评分。应用会实时更新每个选项的评分结果&#xff0c;并统计平均分。这一功能适合用于问卷调查或评分统计的场景。 关键词 UI互动应用评分统计状态管理数据处理多目标评分 一、功能说明 评分统计工具允许用…...

run postinstall error, please remove node_modules before retry!

下载 node_modules 报错&#xff1a;run postinstall error, please remove node_modules before retry! 原因&#xff1a;node 版本出现错误&#xff0c;我的项目之前是在 12 下运行的。解决方法&#xff1a; 先卸载node_modules清除缓存将node版本切换到12重新下载即可...

详细解读TISAX认证的意义

详细解读TISAX认证的意义&#xff0c;犹如揭开信息安全领域的一颗璀璨明珠&#xff0c;它不仅代表了企业在信息安全管理方面的卓越成就&#xff0c;更是通往全球汽车供应链信任桥梁的关键一环。TISAX&#xff0c;即“Trusted Information Security Assessment Exchange”&#…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...