go|context源码解析

news2025/7/27 21:31:11

文章目录

  • Context接口
    • Deadline()
    • Done()
    • Err()
    • Value()
  • canceler接口
  • ctx
    • emptyCtx
    • cancelCtx
    • timerCtx
    • valueCtx
  • 基本使用
    • cancelCtx
    • valueCtx

首先看一下源码对“context”的描述,

When a Context is canceled, all Contexts derived from it are also canceled.

当一个Context被取消时,所有从它派生的Context也会被取消。

The WithCancel, WithDeadline, and WithTimeout functions take a Context (the parent) and return a derived Context (the child) and a CancelFunc. Calling the CancelFunc cancels the child and its children, removes the parent’s reference to the child, and stops any associated timers. Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires. The go vet tool checks that CancelFuncs are used on all control-flow paths.

WithCancel、WithDeadline和WithTimeout函数接受一个Context(父类)并返回一个派生Context(子类)和一个CancelFunc。调用CancelFunc会取消子context及其由该context派生出的子context,移除父context对子context的引用,并停止所有相关的计时器。未能调用CancelFunc会泄漏子context及其由它派生出的子context,直到父context被取消或计时器触发。go - vet工具检查在所有控制流路径上是否使用了CancelFuncs。

Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation:

使用上下文的程序应该遵循这些规则,以保持包之间的接口一致,并使静态分析工具能够检查上下文传播:

Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx:
func DoSomething(ctx context. Context, arg Arg) error {
use ctx
}

不要在结构类型中存储上下文;相反,将上下文显式地传递给需要它的每个函数。Context应该是第一个参数,通常命名为ctx:
func DoSomething(ctx context. Context, arg Arg) error {
use ctx
}

Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use. The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines.Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.

即使函数允许也不要传递一个nil_context,如果不知道使用哪一个context可以传递context.todo[其实就是emptyctx]。
相同的context可以被传递在运行在不同协程中的多个函数上.多个goroutine使用同一个context是并发安全的。
context值应该用于在函数和API之间共享的请求范围数据,比如身份验证令牌、请求ID或跟踪信息等数据。,而不是用于传递可选参数。可选参数应该明确地定义为函数参数,以确保代码的清晰性和可维护性。

接下来分析源码

Context接口

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

可以看出"Context"接口包含了四个方法,接下来依次介绍这四个方法。

Deadline()

Deadline() (deadline time.Time, ok bool)

Deadline()返回当前context应该被取消的时间,如果没有设置取消时间,ok为false。

在这里插入图片描述
运行结果如下

0001-01-01 00:00:00 +0000 UTC false
0001-01-01 00:00:00 +0000 UTC false
2023-11-06 15:44:34.580231 +0800 CST m=+5.003172901 true

Done()

Done() <-chan struct{}

Done返回一个只读chan,由于只读所以只有该通道关闭时才会唤醒所有监听该通道的协程,至于何时关闭,有可能时超时关闭,也有可能通过cancelfunc手动关闭。如果无法被关闭返回回一个nil值。对Done()的连续调用会返回相同的值。
Done被用于select语句

func Stream(ctx context.Context, out chan<- Value) error {
	 	for {
	 		v, err := DoSomething(ctx)
	 		if err != nil {
	 			return err
	 		}
	 		select {
	 		case <-ctx.Done():
	 			return ctx.Err()
	 		case out <- v:
	 		}
	 	}
	 }

Err()

Err() error
  • 通道没有被关闭,返回nil
  • 通道被关闭返回关闭的原因
    如果context被取消返回"Canceled"
    如果超时取消会返回"DeadlineExceeded"
//context被cancelfunc取消返回的err
var Canceled = errors.New("context canceled")

//context因超时被取消
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 }

Value()

Value(key any) any

Value返回context中与key相关联的value值,没有与之关联的value值返回nil,连读的调用返回相同的结果。
键标识context中的特定值。希望在Context中存储值的函数通常在全局变量中分配一个键,然后将该键用作Context的参数。

由于需要使用"key"进行比较所以需要key是可比较的任何类型。并且建议将键定位为未导出的变量,这样可以避免产生冲突。

定义Context键的包应该为使用该键存储的值提供类型安全的访问器:package user定义了存储在context类型中的value值

package user
import "context"
type User struct {...}
type key int    
//未导出的key值,避免冲突;user使用userkey;uservalue存储在context中
var userKey key
//clients use user.NewContext and user.FromContext instead of using this key directly.

//NewContext 返回一个携带u值的context
func NewContext(ctx context.Context, u *User) context.Context {
	return context. WithValue(ctx, userKey, u)
}

//FromContext 根据userkey返回value值
func FromContext(ctx context.Context) (*User, bool) {
 		u, ok := ctx.Value(userKey).(*User)
 		return u, ok
}

接下来看看"Context"的具体实现

canceler接口

//canceler是一个可以被直接取消的context 类型
//具体实现是*cancelCtx 和 *timerCtx
type canceler interface {
	cancel(removeFromParent bool, err, cause error)
	Done() <-chan struct{}
}

ctx

emptyCtx

/*
emptyCtx永远不会被取消,没有值,也没有截止日期。它不是struct{},因为这种类型的变量必须有不同的地址。[就像C++中的empty class的大小并不是0,因为为了保证类对象的每个实例都有唯一的地址,会在empty class中插入一个变量]
*/
type emptyCtx int

接下来看看empty如何实现Context接口的四种方法

//由于emptyCtx没有设置取消时间故直接返回nil和false
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}
//emptyCtx不能被取消
func (*emptyCtx) Done() <-chan struct{} {
	return nil
}

func (*emptyCtx) Err() error {
	return nil
}
//emptyCtx不能存储值
func (*emptyCtx) Value(key any) any {
	return nil
}

emptyCtx有两个具体的变量

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)
func (e *emptyCtx) String() string {
	switch e {
	case background:
		return "context.Background"
	case todo:
		return "context.TODO"
	}
	return "unknown empty Context"
}

/*
Background返回一个非空的empty Context。它永远不会被取消,没有值,也没有截止日期。它通常由main函数、初始化和测试使用,并作为传入请求的根context。
*/
func Background() Context {
	return background
}
/*
TODO返回一个非空的empty Context。当不清楚使用哪个context或context还不可用时(因为周围的函数还没有扩展到接受上下文参数)使用todo
*/
func TODO() Context {
	return todo
}

cancelCtx

首先看一下cancelCtx的结构定义

//cancelCtx可以被取消。当被取消的时候,也会取消实现了
//canceler接口的chidlen context
type cancelCtx struct {
	Context //组合继承Context接口

	mu       sync.Mutex            // protects following fields
	done     atomic.Value          // of chan struct{}, created lazily, closed by first cancel call 用来懒汉式创造chan,被关闭通过第一次的cancel调用
	children map[canceler]struct{} // set to nil by the first cancel call
	err      error                 // set to non-nil by the first cancel call
	cause    error                 // set to non-nil by the first cancel call
}

看一下cancelFunc的定义

//CancelFun不会等work停止,会强制终止当前的work
//可以被多个写成同时调用
//在第一次调用之后,之后的调用CancelFunc什么也不做
type CancelFunc func()

看一下如何获取cancelCtx

/*
WithCancel返回一个副本:父context和一个新的Done通道。
返回的context通道被关闭的时机:
1.调用返回的cancelfunc
2.父context的Done chan被关闭。
取消context会释放它所关联的资源,所以运行在该context的操作完成之后应该调用cancelfunc释放资源
*/
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := withCancel(parent)
	return c, func() { c.cancel(true, Canceled, nil) }
}
func withCancel(parent Context) *cancelCtx {
	if parent == nil {
	// parent context不能为nil
		panic("cannot create context from nil parent")
	}
	// 将c的Context设置为parent
	c := newCancelCtx(parent)
	// 找到parent中的cancelCtx
	// 将c加入到该cancelCtx的child列表
	propagateCancel(parent, c)
	return c
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) *cancelCtx {
	return &cancelCtx{Context: parent}
}
//从这里也可以看出WithCancel(parent ...)返回的是一个带有parent context和new done chan的副本。

接下来看一下withcancel的另外一个函数“propagateCancel”

[propagate传播]
// propagateCancel arranges for child to be canceled when parent is.
//这个函数的作用是将child节点加入到parent节点的childen中
func propagateCancel(parent Context, child canceler) {
	//获取parent的Done()
	done := parent.Done()
	//父context不能被取消,什么也不做
	if done == nil {
		return // parent is never canceled
	}
	
	select {
	case <-done:
		//父context已经被取消,直接取消子context
		// parent is already canceled
		child.cancel(false, parent.Err(), Cause(parent))
		return
	default:
	//放行执行下面的逻辑
	}
	//在parent中找到了*cancelCtx
	if p, ok := parentCancelCtx(parent); ok {
		p.mu.Lock()
		if p.err != nil {
			// parent has already been canceled
			child.cancel(false, p.err, p.cause)
		} else {
			if p.children == nil {
				// 懒惰式创建
				p.children = make(map[canceler]struct{})
			}
			//将child加入到cancelCtx的children中
			p.children[child] = struct{}{}
		}
		p.mu.Unlock()
	} else {
	/*
		// goroutines counts the number of goroutines ever created; 记录创建的协程数量
		var goroutines atomic.Int32
	*/
		goroutines.Add(1)
		//新开启一个协程监听parent.Done()和child.Done()
		go func() {
			select {
			case <-parent.Done():
				child.cancel(false, parent.Err(), Cause(parent))
			case <-child.Done():
			}
		}()
	}
}

/*
parentCancelCtx返回父对象的底层*cancelCtx。它通过查找
parent.Value(&cancelCtxKey)来找到最里面的封闭
*cancelCtx,然后检查parent.Done()是否与*cancelCtx匹配。
(如果没有,*cancelCtx已经被封装在提供不同done通道的自定义实
现中,在这种情况下我们不应该绕过它。)
*/
//&cancelCtxKey is the key that a cancelCtx returns itself for.
var cancelCtxKey int
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
	//获取parent的context
	done := parent.Done()
	/*
		closedchan是一个可重用的已经被关闭的通道
		var closedchan = make(chan struct{})
		func init() {
			close(closedchan)
		}
	*/
	if done == closedchan || done == nil {
		return nil, false
	}
	//在parent中找到cancelCtxKey对应的value值
	p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
	if !ok {
	//没有找到
		return nil, false
	}
	//加载从父context中找到的cancelCtx的chan
	pdone, _ := p.done.Load().(chan struct{})
	//从父context中寻找到的cancelCtx的Done()chan与从parent中的Done()获取到的chan不是同一个,说明parent自定义了Done()方法
	if pdone != done {
		return nil, false
	}
	return p, true
}

看一下cancelCtx对Context接口的实现
cancelCtx并没有实现Deadline方法,所以是继承了parent ctx的Deadline()方法

func (c *cancelCtx) Value(key any) any {
	if key == &cancelCtxKey {
		return c
	}
	return value(c.Context, key)
}
func value(c Context, key any) any {
	for {
		//c一定要是一个interface才可以使用c.(type)
		switch ctx := c.(type) {
		case *valueCtx:
			// valueCtx返回对应的val值
			if key == ctx.key {
				return ctx.val
			}
			c = ctx.Context
		case *cancelCtx:
			// cancelCtx返回自身
			if key == &cancelCtxKey {
				return c
			}
			c = ctx.Context
		case *timerCtx:
			// timerCtx返回其中的cancelCtx
			if key == &cancelCtxKey {
				return ctx.cancelCtx
			}
			c = ctx.Context
		case *emptyCtx:
			return nil
		default:
		//自定义的Value方法
			return c.Value(key)
		}
	}
}
//双重检测创建chan
func (c *cancelCtx) Done() <-chan struct{} {
	//c.done是一个atomic.Value类型的变量,是对结构体类型的原子操作,是并发安全的
	//获取c中存储的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{})
}
func (c *cancelCtx) Err() error {
//因为是并发安全的,所以要加锁进行保护
	c.mu.Lock()
	err := c.err
	c.mu.Unlock()
	return err
}
// cancel sets c.cause to cause if this is the first time c is canceled.
//cancel 调用关闭c.done,取消每一个c的children,
//如果removeFromParent是true,将c从parent的children中移除。
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已经被取消过
		c.mu.Unlock()
		return // already canceled
	}
	//设置c.err
	c.err = err
	c.cause = cause
	//获取c中的chan
	d, _ := c.done.Load().(chan struct{})
	if d == nil {
	//closedchan是一个可重用的已经被关闭的通道
		c.done.Store(closedchan)
	} else {
	//close
		close(d)
	}
	//遍历c.children关闭由c派生出的cancelCtx
	for child := range c.children {
		// NOTE: acquiring the child's lock while holding parent's lock.
		// removeFromParent为true,需要获取父节点的锁,但此时已经持有着父节点的锁,所以为false
		child.cancel(false, err, cause)
	}
	c.children = nil
	c.mu.Unlock()
	//从父context中的children中移除c
	if removeFromParent {
		removeChild(c.Context, c)
	}
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
	p, ok := parentCancelCtx(parent)
	if !ok {
		return
	}
	p.mu.Lock()
	if p.children != nil {
	//将child从parent的children中移除
		delete(p.children, child)
	}
	p.mu.Unlock()
}

timerCtx

首先看一下timerCtx的定义

/*
 timerCtx带有一个计时器和一个截止日期。它嵌入了一个			
 cancelCtx来实现Done和Err。它通过停止计时器,然后委托给
 cancelCtx.cancel来实现取消。
*/
type timerCtx struct {
	*cancelCtx
	timer *time.Timer // 定时器

	deadline time.Time //到期时间
}

timerCtx组合继承了cancelCtx,由cancelCtx实现Done()和Err()方法,Deadline()和cancel()方法由timerCtx自己实现

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
	return c.deadline, true
}
func (c *timerCtx) cancel(removeFromParent bool, err, cause error) {
	c.cancelCtx.cancel(false, err, cause)
	if removeFromParent {
		// Remove this timerCtx from its parent cancelCtx's children.
		removeChild(c.cancelCtx.Context, c)
	}
	c.mu.Lock()
	//关闭定时器
	if c.timer != nil {
		c.timer.Stop()
		c.timer = nil
	}
	c.mu.Unlock()

看看获取*timerCtx的方法"withDeadline"

/*
	withDeadline返回一个带有parent context和截止时间deadline的副本;如果parent context的deadline早于d,WithDeadline(parent,d)等同于WithCancel(parent) ;返回的context chan被关闭的时机:1.到截止时间2.cancelfunc被调用3.parent chan被关闭。
	取消上下文会释放与之关联的资源,所以执行完相关的操作之后要调用cancelfunc释放资源
	
*/
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// parent的过期时间早于child,等价于cancelCtx
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}
	// 找到parent中的cancelCtx
	// 将c加入到该cancelCtx的child列表
	propagateCancel(parent, c)
	//当前时间距离d的间隙
	dur := time.Until(d)
	if dur <= 0 {
	//到期取消
		c.cancel(true, DeadlineExceeded, nil) // deadline has already passed
		return c, func() { c.cancel(false, Canceled, nil) }
	}
	//设置定时器
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
	//到达截止时间dur之后调用自定义的function
		c.timer = time.AfterFunc(dur, func() {
			c.cancel(true, DeadlineExceeded, nil)
		})
	}
	return c, func() { c.cancel(true, Canceled, nil) }
}

另一个创建*timerCtx的方式"withTimeout"

/*
取消context会释放与它相关的资源,所以代码应该
在此context运行的操作完成后立即调用cancel:
func slowOperationWithTimeout(ctx context.Context) (Result, error) {
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()  // releases resources if slowOperation completes before timeout elapses
	return slowOperation(ctx)
}
*/
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

valueCtx

先看一下valueCtx的定义

//valueCtx带有key-value pair,可以存储值但不能被取消;
//只实现自己Value方法,其它的方法由Context 接口实现
type valueCtx struct {
	Context
	key, val any
}

创建*valueCtx的方法"WithValue"

/*
提供的key必须是可比较的,并且不应该是string或者任何其它的内置类型这样可以在使用context的package之间避免冲突。使用WithValue必须定义自己的key类型。
为了避免将key赋值给interface的时候产生额外的分配,应该将key设置为具体的struct或者具体的内置类型;如果想将key设置为可导出[exported]的变量,那么key的静态数据类型应该是指针或者接口类型的,这样可以避免在上下文传递是引起不必要的拷贝。example:
var MyKey *string
var MyKey interface{}
*/
func WithValue(parent Context, key, val any) Context {
	if parent == nil {
		panic("cannot create context from nil parent")
	}
	if key == nil {
		panic("nil key")
	}
	//key一定要是可比较的类型
	//go中不可比较的类型有三个:slice,map,function
	if !reflectlite.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}

*valueCtx实现的Value方法

func (c *valueCtx) Value(key any) any {
	if c.key == key {
		return c.val
	}
	return value(c.Context, key)
}

func value(c Context, key any) any {
	for {
		switch ctx := c.(type) {
		case *valueCtx:
			if key == ctx.key {
				return ctx.val
			}
			c = ctx.Context
		case *cancelCtx:
			if key == &cancelCtxKey {
				return c
			}
			c = ctx.Context
		case *timerCtx:
			if key == &cancelCtxKey {
				return ctx.cancelCtx
			}
			c = ctx.Context
		case *emptyCtx:
			return nil
		default:
			return c.Value(key)
		}
	}
}

以上有关context的源码就分析结束了,看一下各个Ctx的应用

基本使用

cancelCtx

func main() {
	get := func(ctx context.Context) <-chan int {
		ch := make(chan int)
		n := 1
		go func() {
			for {
				select {
				case <-ctx.Done():
					return
				case ch <- n:
					n++
					//default: //不要有这个分支不然这个子goroutine会一直for轮询直到被抢占调度

				}
			}
		}()
		return ch
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()//防止goroutine泄露
	for v := range get(ctx) {
		fmt.Println(v)
		if v == 5 {
			break
		}
	}
}

valueCtx

type keytype string
var key keytype

func main(){
	//WithValue的example,自定义具体的key类型
	f := func(ctx context.Context, key keytype) {
		if v := ctx.Value(key); v != nil {
			fmt.Println("find key ,value", v)
		} else {
			fmt.Println("not find key,", key)
		}
	}
	ctx1 := context.WithValue(context.Background(), key, "GO")
	f(ctx1, key)//find key ,value GO
	f(ctx1, keytype("key"))//not find key, key
}

/*
CancelFuncCause的作用等同于CancelFunc,但是添加了额外的取消原因[cancellation cause],如果context已经被取消不会设置cause
*/
// For example, if childContext is derived from parentContext:
//   - if parentContext is canceled with cause1 before childContext is canceled with cause2,
//     then Cause(parentContext) == Cause(childContext) == cause1
//   - if childContext is canceled with cause2 before parentContext is canceled with cause1,
//     then Cause(parentContext) == cause1 and Cause(childContext) == cause2
type CancelCauseFunc func(cause error)

/*
WithCancelCause 的行为等同于WithCancel,但是返回的是CancelFuncCause而不是CancelFunc。调用CancelFuncCause并传入一个非nil的error,可以通过Cause(ctx)进行检索。如果调用CancelFuncCause时传入nil,cause会被设置为Canceled与err记录的值一样
*/
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) {
	c := withCancel(parent)
	return c, func(cause error) { c.cancel(true, Canceled, cause) }
}

使用例子

ctx, cancel := context.WithCancelCause(parent)
cancel(myError)
ctx.Err() // returns context.Canceled
context.Cause(ctx) // returns myError

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

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

相关文章

如何在PowerBI中使用Analyze in Excel

如何在PowerBI中使用Analyze in Excel 之前分享过如何使用DAXStudio将PowerBI与Excel连接 &#xff0c;今天介绍另外一个工具&#xff0c;也可以实现同样的功能&#xff0c;Analyze in Excel。 使用Analyze in Excel 第一步&#xff1a; 首先准备好一个PBIX文件&#xff0c…

大模型分布式训练笔记(基于accelerate+deepspeed分布式训练解决方案)

文章目录 一、分布式训练基础与环境配置&#xff08;1&#xff09;分布式训练简介&#xff08;2&#xff09;如何进行分布式训练&#xff08;3&#xff09;分布式训练环境配置 二、数据并行-原理与实战&#xff08;pytorch框架的nn.DataParallel&#xff09;1&#xff09;data …

鸿蒙UI开发——组件的自适应拉伸

1、概 述 针对常见的开发场景&#xff0c;ArkUI开发框架提供了非常多的自适应布局能力&#xff0c;这些布局可以独立使用&#xff0c;也可多种布局叠加使用。本文针对ArkUI提供的拉伸能力做简单讨论。 拉伸能力是指容器组件尺寸发生变化时&#xff0c;增加或减小的空间全部分…

鸿蒙仓颉语言开发教程:自定义弹窗

假期第一天&#xff0c;祝大家端午节快乐。昨天观看了时代旗舰尊界S800的发布&#xff0c;不得不感慨这车真好啊&#xff5e; 放假闲来无事&#xff0c;继续跟大家分享仓颉语言的开发教程&#xff0c;今天介绍一下自定义弹窗。 仓颉语言中的自定义弹窗和ArkTs类似&#xff0c…

Python 数据分析与可视化实战:从数据清洗到图表呈现

目录 一、数据采集与初步探索 二、数据清洗的七种武器 1. 缺失值处理策略 2. 异常值检测与修正 3. 数据类型转换技巧 三、数据转换的魔法工坊 1. 透视表与交叉表 2. 窗口函数实战 3. 文本数据处理 四、可视化呈现的艺术 1. 基础图表进阶用法 2. 高级可视化方案 3.…

机器学习数据降维方法

1.数据类型 2.如何选择降维方法进行数据降维 3.线性降维&#xff1a;主成分分析&#xff08;PCA&#xff09;、线性判别分析&#xff08;LDA&#xff09; 4.非线性降维 5.基于特征选择的降维 6.基于神经网络的降维 数据降维是将高维数据转换为低维表示的过程&#xff0c;旨在保…

uefi和legacy有什么区别_从几方面分析uefi和legacy的区别

uefi和legacy是两种不同的引导方式,uefi是新式的BIOS&#xff0c;legacy是传统BIOS。你在UEFI模式下安装的系统&#xff0c;只能用UEFI模式引导&#xff1b;同理&#xff0c;如果你是在Legacy模式下安装的系统&#xff0c;也只能在legacy模式下进系统。uefi只支持64为系统且磁盘…

Spring @Autowired自动装配的实现机制

Spring Autowired自动装配的实现机制 Autowired 注解实现原理详解一、Autowired 注解定义二、Qualifier 注解辅助指定 Bean 名称三、BeanFactory&#xff1a;按类型获取 Bean四、注入逻辑实现五、小结 源码见&#xff1a;mini-spring Autowired 注解实现原理详解 Autowired 的…

一种基于性能建模的HADOOP配置调优策略

1.摘要 作为分布式系统基础架构的Hadoop为应用程序提供了一组稳定可靠的接口。该文作者提出了一种基于集成学习建模的Hadoop配置参数调优的方法。实验结果表明&#xff0c;该性能模型可以准确预测MapReduce应用程序的运行时间。采用提出的Hadoop配置参数方法调优后&#xff0c…

【Stable Diffusion 1.5 】在 Unet 中每个 Cross Attention 块中的张量变化过程

系列文章目录 文章目录 系列文章目录前言特征图和注意力图的尺寸差异原因在Break-a-Scene中的具体实现总结 前言 特征图 (Latent) 尺寸和注意力图(attention map)尺寸在扩散模型中有差异&#xff0c;是由于模型架构和注意力机制的特性决定的。 特征图和注意力图的尺寸差异原…

MySQL - Windows 中 MySQL 禁用开机自启,并在需要时手动启动

Windows 中 MySQL 禁用开机自启&#xff0c;并在需要时手动启动 打开服务管理器&#xff1a;在底部搜索栏输入【services.msc】 -> 点击【服务】 打开 MySQL 服务的属性管理&#xff1a;找到并右击 MySQL 服务 -> 点击【属性】 此时的 MySQL 服务&#xff1a;正在运行&a…

OpenCV CUDA模块霍夫变换------在 GPU 上执行概率霍夫变换检测图像中的线段端点类cv::cuda::HoughSegmentDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::HoughSegmentDetector 是 OpenCV 的 CUDA 模块中一个非常重要的类&#xff0c;它用于在 GPU 上执行 概率霍夫变换&#xff08;Probabi…

ck-editor5的研究 (5):优化-页面离开时提醒保存,顺便了解一下 Editor的生命周期 和 6大编辑器类型

前言 经过前面的 4 篇内容&#xff0c;我们已经慢慢对 CKEditor5 熟悉起来了。这篇文章&#xff0c;我们就来做一个优化&#xff0c;顺便再补几个知识点&#xff1a; 当用户离开时页面时&#xff0c;提醒他保存数据了解一下 CKEditor5 的 六大编辑器类型了解一下 editor 实例对…

[3D GISMesh]三角网格模型中的孔洞修补算法

&#x1f4d0; 三维网格模型空洞修复技术详解 三维网格模型在扫描、重建或传输过程中常因遮挡、噪声或数据丢失产生空洞&#xff08;即边界非闭合区域&#xff09;&#xff0c;影响模型的完整性与可用性。空洞修复&#xff08;Hole Filling&#xff09;是计算机图形学和几何处…

11.2 java语言执行浅析3美团面试追魂七连问

美团面试追魂七连问&#xff1a;关于Object o New Object() ,1请解释一下对象的创建过程(半初始化) 2,加问DCL要不要volatile 问题(指令重排) 3.对象在内存中的存储布局(对象与数组的存储不同),4.对象头具体包括什么.5.对象怎么定位.6.对象怎么分配(栈-线程本地-Eden-Old)7.在…

MySQL 全量、增量备份与恢复

一.MySQL 数据库备份概述 备份的主要目的是灾难恢复&#xff0c;备份还可以测试应用、回滚数据修改、查询历史数据、审计等。之前已经学习过如何安装 MySQL&#xff0c;本小节将从生产运维的角度了解备份恢复的分类与方法。 1 数据备份的重要性 在企业中数据的价值至关…

MonoPCC:用于内窥镜图像单目深度估计的光度不变循环约束|文献速递-深度学习医疗AI最新文献

Title 题目 MonoPCC: Photometric-invariant cycle constraint for monocular depth estimation of endoscopic images MonoPCC&#xff1a;用于内窥镜图像单目深度估计的光度不变循环约束 01 文献速递介绍 单目内窥镜是胃肠诊断和手术的关键医学成像工具&#xff0c;但其…

SpringAI系列 - MCP篇(三) - MCP Client Boot Starter

目录 一、Spring AI Mcp集成二、Spring AI MCP Client Stater三、spring-ai-starter-mcp-client-webflux集成示例3.1 maven依赖3.2 配置说明3.3 集成Tools四、通过SSE连接MCP Server五、通过STDIO连接MCP Server六、通过JSON文件配置STDIO连接一、Spring AI Mcp集成 Spring AI…

【深度学习新浪潮】以Dify为例的大模型平台的对比分析

我们从核心功能、适用群体、易用性、可扩展性和安全性五个维度展开对比分析: 一、核心功能对比 平台核心功能多模型支持插件与工具链Dify低代码开发、RAG增强、Agent自律执行、企业级安全支持GPT-4/5、Claude、Llama3、Gemini及开源模型(如Qwen-VL-72B),支持混合模型组合可…

Asp.net core 使用EntityFrame Work

安装以下Nuget 包 Microsoft.EntityFrameworkCore.Tools Microsoft.EntityFrameworkCore.Design Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer或者Npgsql.EntityFrameworkCore.PostgreSQL 安装完上述Nuget包之后,在appset…