Golang的context

news2025/1/13 7:57:00

目录

context的基本使用

为什么需要context

Context interface 

 标准 error

emptyCtx 

cancelCtx 

Deadline 方法

Done 方法

Err 方法

Value 方法

context.WithCancel()

newCancelCtx

WithCancel中propagateCancel

cancel

timerCtx

valueCtx  


context的基本使用

创建:

context.Background():创建一个空的父类context

context.TODO(): 创建一个未来 可能有内容的父类 context,有扩展性

	// 创建一个可以取消的Context
	ctxCancel, cancelCancel := context.WithCancel(context.Background())
    defer cancelCancel() // 当不再需要时,确保调用cancel来释放资源

    // 创建一个带有截止时间的Context
    deadline := time.Now().Add(time.Second * 10)
 	ctxDeadline, cancelDeadline := context.WithDeadline(context.Background(), deadline)
 	defer cancelDeadline()


    // 创建一个带有超时时间的Context
 	timeout := 10 * time.Second
 	ctxTimeout, cancelTimeout := context.WithTimeout(context.Background(), timeout)
 	defer cancelTimeout()

返回值: Context 上下文本体 和 CancelFunc 关闭Context 的函数

类别:

context.WithCancel 创建一个可以取消的Context,当调用 返回值 cancel 时 就可以通知关闭

context.WithDeadline 创建一个带有截止时间的Context,到一个特定时间时便发出通知

context.WithTimeout 创建一个带有超时时间的Context,过了一段时间后就发出通知

防止go协程泄漏使用例子

package main

import (
	"context"
	"fmt"
)

func main() {
	//WithCancel(ctx Context, cancel CancelFunc)=(名 Context,处理函数 CancelFunc)
	ctx, cancel := context.WithCancel(context.Background()) //context.Background() 处理 Goroutine
	context.TODO()
	ch := func(ctx context.Context) <-chan int {
		ch := make(chan int)
		go func() {
			for i := 0; ; i++ {
				select {
				case <-ctx.Done():
					return
				case ch <- i:
				}
			}
		}()
		return ch
	}(ctx)

	for v := range ch {
		fmt.Println(v)
		if v == 5 {
			cancel()
			break
		}
	}
}

为什么需要context

场景1: 当主协程启动了m个子协程,m个子协程又启动更多的协程,

那监控起来需要很多的channel, 操作非常繁琐。

如果我们使用 context 时,当父类的context关闭,子类也会一起关闭以此类推,类似Qt中的对象树

场景2:任务A 挂在 任务B 下,我们希望 B 有着定时退出的功能,而且当B退出时A也需要退出

使用定时器+channel时,就显的有些繁琐,我们可以直接使用context.WithTimeout 一步到位

Context interface 

golang的接口 类似 c++的基类

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

Context 为 interface,定义了四个核心 api:

• Deadline:返回 context 的过期时间;

• Done:返回 context 中的 channel;

• Err:返回错误;

• Value:返回 context 中的对应 key 的值.

 标准 error

var Canceled = errors.New("context canceled")

var DeadlineExceeded error = deadlineExceededError{}

type deadlineExceededError struct{}

func (deadlineExceededError) Error() string   { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool   { return true }
func (deadlineExceededError) Temporary() bool { return true

• Canceled:context 被 cancel 时会报此错误;

• DeadlineExceeded:context 超时时会报此错误.

emptyCtx 

重写了接口 api:

type emptyCtx int

func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
    return
}

func (*emptyCtx) Done() <-chan struct{} {
    return nil
}

func (*emptyCtx) Err() error {
    return nil
}

func (*emptyCtx) Value(key any) any {
    return 
}

返回的什么东西都为空 

• emptyCtx 是一个空的 context,本质上类型为一个整型;

• Deadline 方法会返回一个公元元年时间以及 false 的 flag,标识当前 context 不存在过期时间;

• Done 方法返回一个 nil 值,用户无论往 nil 中写入或者读取数据,均会陷入阻塞;

• Err 方法返回的错误永远为 nil;

• Value 方法返回的 value 同样永远为 nil.

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx)
)

func Background() Context {
    return background
}

func TODO() Context {
    return todo
}

 当使用context.Background() & context.TODO() 创建emptyCtx 时返回固定的 emptyCtx 

cancelCtx 

type cancelCtx struct {
    Context
   
    mu       sync.Mutex            // protects following fields
    done     atomic.Value          // of chan struct{}, created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

type canceler interface {
    cancel(removeFromParent bool, err error)
    Done() <-chan struct{}
}

Deadline 方法

cancelCtx 未实现该方法,仅是 embed 了一个带有 Deadline 方法的 Context interface,因此倘若直接调用会报错.


Done 方法

流程如下:

源码:

func (c *cancelCtx) Done() <-chan struct{} {
    d := c.done.Load()
    if d != nil {
        return d.(chan struct{})
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    d = c.done.Load()
    if d == nil {
        d = make(chan struct{})
        c.done.Store(d)
    }
    return d.(chan struct{})
}


• 基于 atomic 包,读取 cancelCtx 中的 chan;倘若已存在,则直接返回;

• 加锁后,在此检查 chan 是否存在,若存在则返回;(双重检查 double check)

• 初始化 chan 存储到 aotmic.Value 当中,并返回.(懒加载机制 懒汉)

 

Err 方法

func (c *cancelCtx) Err() error {
    c.mu.Lock()
    err := c.err
    c.mu.Unlock()
    return err
}

• 加锁;

• 读取 cancelCtx.err;

• 解锁;

• 返回结果;

Value 方法

func (c *cancelCtx) Value(key any) any {
    if key == &cancelCtxKey {
        return c
    }
    return value(c.Context, key)
}

• 倘若 key 特定值 &cancelCtxKey,则返回 cancelCtx 自身的指针;

• 否则遵循 valueCtx 的思路取值返回;

context.WithCancel()

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    if parent == nil {
        panic("cannot create context from nil parent")
    }
    c := newCancelCtx(parent)
    propagateCancel(parent, &c)
    return &c, func() { c.cancel(true, Canceled) }
}

• 校验父 context 非空;

• 注入父 context 构造好一个新的 cancelCtx;

• 在 propagateCancel 方法内启动一个守护协程,以保证父 context 终止时,该 cancelCtx 也会被终止;

• 将 cancelCtx 返回,连带返回一个用以终止该 cancelCtx 的闭包函数.

newCancelCtx

没用propagateCancel 方法

func newCancelCtx(parent Context) cancelCtx {
    return cancelCtx{Context: parent}
}

• 注入父 context 后,返回一个新的 cancelCtx.

WithCancel中propagateCancel

propagateCancel 传播取消

func propagateCancel(parent Context, child canceler) {
    done := parent.Done()
    if done == nil {
        return // parent is never canceled
    }

    select {
    case <-done:
        // parent is already canceled
        child.cancel(false, parent.Err())
        return
    default:
    }

    if p, ok := parentCancelCtx(parent); ok {
        p.mu.Lock()
        if p.err != nil {
            // parent has already been canceled
            child.cancel(false, p.err)
        } else {
            if p.children == nil {
                p.children = make(map[canceler]struct{})
            }
            p.children[child] = struct{}{}
        }
        p.mu.Unlock()
    } else {
        atomic.AddInt32(&goroutines, +1)
        go func() {
            select {
            case <-parent.Done():
                child.cancel(false, parent.Err())
            case <-child.Done():
            }
        }()
    }
}

cancel

// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
// cancel sets c.cause to cause if this is the first time c is canceled.
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {
	if err == nil {
		panic("context: internal error: missing cancel error")
	}
	if cause == nil {
		cause = err
	}
	c.mu.Lock()
	if c.err != nil {
		c.mu.Unlock()
		return // already canceled
	}
	c.err = err
	c.cause = cause
	d, _ := c.done.Load().(chan struct{})
	if d == nil {
		c.done.Store(closedchan)
	} else {
		close(d)
	}
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		child.cancel(false, err, cause)
	}
	c.children = nil
	c.mu.Unlock()

	if removeFromParent {
		removeChild(c.Context, c)
	}
}

timerCtx

只介绍类,其他与cancel差不多,加上了过期时刻


type timerCtx struct {
    cancelCtx
    timer *time.Timer // Under cancelCtx.mu.

    deadline time.Time
}

valueCtx  

键值对 Ctx

type valueCtx struct {
    Context
    key, val any
}

参考 : 小徐先生1212 --context 底层实现 

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

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

相关文章

宽睿数字平台兼容TDengine 等多种数据库,提供行情解决方案

小T导读&#xff1a;最近&#xff0c;涛思数据与宽睿金融宣布了一项重要合作。在此之前&#xff0c;宽睿金融对 TDengine 进行了性能测试&#xff0c;并根据测试报告的结果&#xff0c;决定将 TDengine 接入宽睿数字平台&#xff0c;以提升高密度行情处理效率。本文将详细介绍此…

智慧监狱大数据整体解决方案(51页PPT)

方案介绍&#xff1a; 智慧监狱大数据整体解决方案通过集成先进的信息技术和大数据技术&#xff0c;实现对监狱管理的全面升级和智能化改造。该方案不仅提高了监狱管理的安全性和效率&#xff0c;还提升了监狱的智能化水平&#xff0c;为监狱的可持续发展提供了有力支持。 部…

【Vue】获取模块内的actions方法

目标&#xff1a; 掌握模块中 action 的调用语法 (同理 - 直接类比 mutation 即可) 注意&#xff1a; 默认模块中的 mutation 和 actions 会被挂载到全局&#xff0c;需要开启命名空间&#xff0c;才会挂载到子模块。 调用语法&#xff1a; 直接通过 store 调用 $store.di…

Flutter - Material3适配

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - Material3适配 对比图具体实现一些组件的变化 代码实现Material2的ThemeDataMaterial3的ThemeData Material3适配官方文档 flutter SDK升级到3.16.0之后 …

ES启动失败原因记录

一、JDK不兼容&#xff1a; es和jdk是一个强依赖的关系&#xff0c;所以当我们在新版本的ElasticSearch压缩包中包含有自带的jdk&#xff0c;但是当我们的Linux中已经安装了jdk之后&#xff0c;就会发现启动es的时候优先去找的是Linux中已经装好的jdk&#xff0c;此时如果jdk的…

35、matlab设置字体、查看工具包版本、窗口默认布局和程序发布

1、matlab设置字体 1&#xff09;找到预设并点击预设 2&#xff09;设置流程&#xff1a;字体——>自定义——>编辑器——>选择字体及格式——>确定 如图序号所示 2、matlab查看工具包版本&#xff1a;ver命令 1&#xff09;命令行窗口输入命令 即可查看工具包…

反射...

一、反射的定义 二、获取Class对象三种方式 全类名&#xff1a;包名类名。 public class test {public static void main(String [] args) throws ClassNotFoundException {//第一种方式Class class1Class.forName("test02.Student");//第二种方法Class class2Stud…

03-240605-Spark笔记

03-240605 1. 行动算子-1 reduce 聚合 格式: def reduce(f: (T, T) > T): T 例子&#xff1a; val sparkConf new SparkConf().setMaster("local[*]").setAppName("Operator")val sc new SparkContext(sparkConf) ​val rdd sc.makeRDD(List(1…

最好用的搜题软件大学?8个公众号和软件推荐清单! #知识分享#知识分享#经验分享

今天&#xff0c;我将分享一些受欢迎的、被大学生广泛使用的日常学习工具&#xff0c;希望能给你的学习生活带来一些便利和启发。 1.彩虹搜题 这个是公众号 一款专供大学生使用的搜题神器专注于大学生校内学习和考研/公考等能力提升 下方附上一些测试的试题及答案 1、行大量…

通过 CartPole 游戏详细说明 PPO 优化过程

CartPole 介绍 在一个光滑的轨道上有个推车&#xff0c;杆子垂直微置在推车上&#xff0c;随时有倒的风险。系统每次对推车施加向左或者向右的力&#xff0c;但我们的目标是让杆子保持直立。杆子保持直立的每个时间单位都会获得 1 的奖励。但是当杆子与垂直方向成 15 度以上的…

Vue3【十七】props的作用和组件之间的传值限定类型和默认值

Vue3【十七】props的作用和组件之间的传值限定类型和默认值 Vue3【十七】props的作用和组件之间的传值限定类型和默认值 父组件传值给子组件 多个值传递 传值限定类型和 默认值 实例截图 目录结构 代码 person.vue <template><div class"person"><p…

硬件开发笔记(十七):RK3568底板电路串口、485、usb原理图详解

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139589308 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

格式工厂 v5 解锁版 (免费多媒体文件转换工具)

前言 格式工厂是免费多功能的多媒体文件转换工具&#xff0c;轻松转换一切你想要的格式。利器在手&#xff0c;转换不愁&#xff01;支持几乎所有类型格式的相互转换&#xff0c;各种视频、音频、图片、PDF文档等格式&#xff0c;转换视频过程中&#xff0c;可以修复损坏的文件…

Cesium离线部署影像+地形:从0到1

Cesium加载本地影像地形 本教程记录的是小白从0-1搭建本地cesium服务的过程&#xff0c;踩的各种坑通过查找资料都一一填补&#xff0c;最终达到的效果是在本地上能够跑官网飞机航线的例子。效果如下&#xff1a; 主要流程如下&#xff1a; 1、下载离线地图和地形2、nginx部署…

工业机器人远程运维,增强智慧工厂运营管理

1、需求背景 随着工业自动化技术的普及和工业机器人应用的增加&#xff0c;制造业对于生产线稳定性和效率的要求不断提高。然而&#xff0c;传统的现场监控方式存在着地理位置限制、实时监控难度大以及诊断能力有限等问题&#xff0c;迫切需要一种更具灵活性和效率的监控方式。…

FFMpeg解复用流程

文章目录 解复用流程图复用器与解复用器小结 解复用流程图 流程图&#xff0c;如上图所示。 复用器与解复用器 复用器&#xff0c;就是视频流&#xff0c;音频流&#xff0c;字幕流&#xff0c;其他成分&#xff0c;按照一定规则组合成视频文件&#xff0c;视频文件可以是mp4…

“百变换装师”之证照之星

拍证件照是一件很麻烦的事吗&#xff1f;证件照编辑是一件复杂的事吗&#xff1f;只有专业人员才能对证件照进行编辑吗&#xff1f;以前可能是&#xff0c;但今天小编将给大家分享一个证件照编辑软件证照之星&#xff0c;它将使每一个人都能具备简单的证件照编辑技能。 证照之星…

cve_2017_12635-CouchDB垂直权限绕过

1.采用参考 https://www.cnblogs.com/mlxwl/p/16577781.html vulfocus&#xff1a;Vulfocus 漏洞威胁分析平台 2.产生原因 在2017年11月15日&#xff0c;CVE-2017-12635和CVE-2017-12636披露&#xff0c;CVE-2017-12635是由于Erlang和JavaScript对JSON解析方式的不同&#…

优优嗨聚集团:卤味市场新风向,创新融合与品质升级引领未来发展

卤味市场作为中国传统美食文化的重要组成部分&#xff0c;近年来呈现出蓬勃发展的态势。随着消费者口味的不断变化和市场的日益竞争&#xff0c;卤味行业正面临着前所未有的机遇与挑战。那么&#xff0c;卤味市场的未来发展将何去何从&#xff1f;本文将从创新融合和品质升级两…

Python 深度学习和机器学习的模型评估库之torchmetrics使用详解

概要 在深度学习和机器学习项目中,模型评估是一个至关重要的环节。为了准确地评估模型的性能,开发者通常需要计算各种指标(metrics),如准确率、精确率、召回率、F1 分数等。torchmetrics 是一个用于 PyTorch 的开源库,提供了一组方便且高效的评估指标计算工具。本文将详…