返回顶部
首页 > 资讯 > 后端开发 > GO >对Go语言中的context包源码分析
  • 594
分享到

对Go语言中的context包源码分析

2024-04-02 19:04:59 594人浏览 薄情痞子
摘要

目录一、包说明分析二、包结构分析三、Context接口类型分析四、后续分析规划五、基于实现类型到常用函数六、With-系列函数七、扩展功能以及如何扩展八、补充一、包说明分析 cont

一、包说明分析

context包:这个包分析的是1.15

context包定义了一个Context类型(接口类型),通过这个Context接口类型, 就可以跨api边界/跨进程传递一些deadline/cancel信号/request-scoped值.

发给server的请求中需要包含Context,server需要接收Context. 在整个函数调用链中,Context都需要进行传播. 期间是可以选择将Context替换为派生Context(由With-系列函数生成). 当一个Context是canceled状态时,所有派生的Context都是canceled状态.

With-系列函数(不包含WithValue)会基于父Context来生成一个派生Context, 还有一个CancelFunc函数,调用这个CancelFun函数可取消派生对象和 "派生对象的派生对象的...",并且会删除父Context和派生Context的引用关系, 最后还会停止相关定时器.如果不调用CancelFunc,直到父Context被取消或 定时器触发,派生Context和"派生Context的派生Context..."才会被回收, 否则就是泄露leak. Go vet工具可以检测到泄露.

使用Context包的程序需要遵循以下以下规则,目的是保持跨包兼容, 已经使用静态分析工具来检查context的传播:

  • Context不要存储在struct内,直接在每个函数中显示使用,作为第一个参数,名叫ctx
  • 即使函数允许,也不要传递nil Context,如果实在不去确定就传context.TODO
  • 在跨进程和跨api时,要传request-scoped数据时用context Value,不要传函数的可选参数
  • 不同协程可以传递同一Context到函数,多协程并发使用Context是安全

二、包结构分析

核心的是:

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
    func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
    func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
    type CancelFunc
    type Context

从上可以看出,核心的是Context接口类型,围绕这个类型出现了With-系列函数, 针对派生Context,还有取消函数CancelFunc.

还有两个暴露的变量:

Canceled

context取消时由Context.Err方法返回

DeadlineExceeded

context超过deadline时由Context.Err方法返回

三、Context接口类型分析

context也称上下文.

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

先看说明:

跨api时,Context可以携带一个deadline/一个取消信号/某些值. 并发安全.

方法集分析:

Deadline

  • 返回的是截至时间
  • 这个时间表示的是任务完成时间
  • 到这个时间点,Context的状态已经是be canceled(完成状态)
  • ok为false表示没有设置deadline
  • 连续调用,返回的结果是相同的

Done

  • 返回的只读信道
  • 任务完成,信道会被关闭,Context状态是be canceled
  • Conetext永远不be canceled,Done可能返回nil
  • 连续调用,返回的结果是相同的
  • 信道的关闭会异步发生,且会在取消函数CancelFunc执行完之后发生
  • 使用方面,Done需要配合select使用
  • 更多使用Done的例子在这个博客

Err

  • Done还没关闭(此处指Done返回的只读信道),Err返回nil
  • Done关闭了,Err返回non-nil的error
  • Context是be canceled,Err返回Canceled(这是之前分析的一个变量)
  • 如果是超过了截至日期deadline,Err返回DeadlineExceeded
  • 如果Err返回non-nil的error,后续再次调用,返回的结果是相同的

Value

  • 参数和返回值都是interface{}类型(这种解耦方式值得学习)
  • Value就是通过key找value,如果没找到,返回nil
  • 连续调用,返回的结果是相同的
  • 上下文值,只适用于跨进程/跨api的request-scoped数据
  • 不适用于代替函数可选项
  • 一个上下文中,一个key对应一个value
  • 典型用法:申请一个全局变量来放key,在context.WithValue/Context.Value中使用
  • key应该定义为非暴露类型,避免冲突
  • 定义key时,应该支持类型安全的访问value(通过key)
  • key不应该暴露
    • 表示应该通过暴露函数来进行隔离(具体可以查看源码中的例子)

四、后续分析规划

看完Context的接口定义后,还需要查看With-系列函数才能知道context的定位, 在With-系列中会涉及到Context的使用和内部实现,那就先看WithCancel.

withCancel:

  • CancelFunc
  • newCancelCtx
    • cancelCtx
  • canceler
  • propagateCancel
    • parentCancelCtx

以下是分析出的通过规则:很多包对外暴露的是接口类型和几个针对此类型的常用函数. 接口类型暴露意味可扩展,但是想扩展之后继续使用常用函数,那扩展部分就不能 修改常用函数涉及的部分,当然也可以通过额外的接口继续解耦. 针对"暴露接口和常用函数"这种套路,实现时会存在一个非暴露的实现类型, 常用函数就是基于这个实现类型实现的.在context.go中的实现类型是emptyCtx. 如果同时需要扩展接口和常用函数,最好是重新写一个新包.

下面的分析分成两部分:基于实现类型到常用函数;扩展功能以及如何扩展.

五、基于实现类型到常用函数

Context接口的实现类型是emptyCtx. 

  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 interface{}) interface{} { return nil }

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

可以看到emptyCtx除了实现了context.Context接口,还实现了context.stringer接口, 注意下面的String不是实现的fmt.Stringer接口,而是未暴露的context.stringer接口. 正如empty的名字,对Context接口的实现都是空的,后续需要针对emptyCtx做扩展.

    var (
      background = new(emptyCtx)
      todo       = new(emptyCtx)
    )
    func Background() Context {
      return background
    }
    func TODO() Context {
      return todo
    }

这里通过两个暴露的函数创建两个空的emptyCtx实例,后续会根据不同场景来扩展. 在注释中,background实例的使用场景是:main函数/初始化/测试/或者作为top-level 的Context(派生其他Context);todo实例的使用场景是:不确定时用todo. 到此emptyCtx的构造就理顺了,就是Background()/TODO()两个函数,之后是针对她们 的扩展和Context派生.

Context派生是基于With-系列函数实现的,我们先看对emptyCtx的扩展, 这些扩展至少会覆盖一部分函数,让空的上下文变成支持某些功能的上下文, 取消信号/截至日期/值,3种功能的任意组合.

从源码中可以看出,除了emptyCtx,还有cancelCtx/myCtx/myDoneCtx/otherContext/ timeCtx/valueCtx,他们有个共同特点:基于Context组合的新类型, 我们寻找的对emptyCtx的扩展,就是在这些新类型的方法中.

小技巧:emptyCtx已经实现了context.Context,如果要修改方法的实现, 唯一的方法就是利用Go的内嵌进行方法的覆盖.简单点说就是内嵌到struct, struct再定义同样签名的方法,如果不需要数据,内嵌到接口也是一样的.

cancelCtx

支持取消信号的上下文

type cancelCtx struct {
  Context

  mu       sync.Mutex
  done     chan struct{}
  children map[canceler]struct{}
  err      error
}

看下方法:

  var cancelCtxKey int
    func (c *cancelCtx) Value(key interface{}) interface{} {
      if key == &cancelCtxKey {
        return c
      }
      return c.Context.Value(key)
    }
    func (c *cancelCtx) Done() <-chan struct{} {
      c.mu.Lock()
      if c.done == nil {
        c.done = make(chan struct{})
      }
      d := c.done
      c.mu.Unlock()
      return d
    }
    func (c *cancelCtx) Err() error {
      c.mu.Lock()
      err := c.err
      c.mu.Unlock()
      return err
    }

cancelCtxKey默认是0,Value()要么返回自己,要么调用上下文Context.Value(), 具体使用后面再分析;Done()返回cancelCtx.done;Err()返回cancelCtx.err;

    func contextName(c Context) string {
      if s, ok := c.(stringer); ok {
        return s.String()
      }
      return reflectlite.TypeOf(c).String()
    }
    func (c *cancelCtx) String() string {
      return contextName(c.Context) + ".WithCancel"
    }

internal/reflectlite.TypeOf是获取接口动态类型的反射类型, 如果接口是nil就返回nil,此处是获取Context的类型, 从上面的分析可知,顶层Context要么是background,要么是todo, cancelCtx实现的context.stringer要么是context.Background.WithCancel, 要么是context.TODO.WithCancel.这里说的只是顶层Context下的, 多层派生Context的结构也是类似的.

值得注意的是String()不属于Context接口的方法集,而是emptyCtx对 context.stringer接口的实现,cancelCxt内嵌的Context,所以不会覆盖 emptyCtx对String()的实现. 

   var closedchan = make(chan struct{})
    func init() {
      close(closedchan)
    }

    func (c *cancelCtx) cancel(removeFromParent bool, err error) {
      if err == nil {
        panic("context: internal error: missing cancel error")
      }
      c.mu.Lock()
      if c.err != nil {
        c.mu.Unlock()
        return // already canceled
      }
      c.err = err
      if c.done == nil {
        c.done = closedchan
      } else {
        close(c.done)
      }
      for child := range c.children {
        // NOTE: acquiring the child's lock while holding parent's lock.
        child.cancel(false, err)
      }
      c.children = nil
      c.mu.Unlock()

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

cancel(),具体的取消信令对应的操作,err不能为nil,err会存到cancelCtx.err, 如果已经存了,表示取消操作已经执行.关闭done信道,如果之前没有调用Done() 来获取done信道,就返回一个closedchan(这是要给已关闭信道,可重用的), 之后是调用children的cancel(),最后就是在Context树上移除当前派生Context.

    func parentCancelCtx(parent Context) (*cancelCtx, bool) {
      done := parent.Done()
      if done == closedchan || done == nil {
        return nil, false
      }
      p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
      if !ok {
        return nil, false
      }
      p.mu.Lock()
      ok = p.done == done
      p.mu.Unlock()
      if !ok {
        return nil, false
      }
      return p, true
    }
    func removeChild(parent Context, child canceler) {
      p, ok := parentCancelCtx(parent)
      if !ok {
        return
      }
      p.mu.Lock()
      if p.children != nil {
        delete(p.children, child)
      }
      p.mu.Unlock()
    }

removeChild首先判断父Context是不是cancelCtx类型, 再判断done信道和当前Context的done信道是不是一致的, (如果不一致,说明:done信道是diy实现的,就不能删掉了).

到此,cancelCtx覆盖了cancelCtx.Context的Done/Err/Value, 同时实现了自己的打印函数String(),还实现了cancel(). 也就是说cancelCtx还实现了接口canceler:

type canceler interface {
  cancel(removeFromParent bool, err error)
  Done() <-chan struct{}
}
// cancelCtx.children的定义如下:
// children map[canceler]struct{}

执行取消信号对应的操作时,其中有一步就是执行children的cancel(), children的key是canceler接口类型,所以有对cancel()的实现. cancelCtx实现了canceler接口,那么在派生Context就可以嵌套很多层, 或派生很多个cancelCtx.

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

非暴露的构造函数.

回顾一下:cancelCtx添加了Context对取消信号的支持. 只要触发了"取消信号",使用方只需要监听done信道即可.

myCtx myDoneCtx otherContext属于测试,等分析测试的时候再细说.

timerCtx

前面说到了取消信号对应的上下文cancelCtx,timerCtx就是基于取消信号上下扩展的

type timerCtx struct {
  cancelCtx
  timer *time.Timer

  deadline time.Time
}

注释说明:内嵌cancelCtx是为了复用Done和Err,扩展了一个定时器和一个截至时间, 在定时器触发时触发cancelCtx.cancel()即可.

  func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
      return c.deadline, true
    }
    func (c *timerCtx) String() string {
      return contextName(c.cancelCtx.Context) + ".WithDeadline(" +
        c.deadline.String() + " [" +
        time.Until(c.deadline).String() + "])"
    }
    func (c *timerCtx) cancel(removeFromParent bool, err error) {
      c.cancelCtx.cancel(false, err)
      if removeFromParent {
        removeChild(c.cancelCtx.Context, c)
      }
      c.mu.Lock()
      if c.timer != nil {
        c.timer.Stop()
        c.timer = nil
      }
      c.mu.Unlock()
    }

timerCtx内嵌了cancelCtx,说明timerCtx也实现了canceler接口, 从源码中可以看出,cancel()是重新实现了,String/Deadline都重新实现了.

cancel()中额外添加了定时器的停止操作.

这里没有deadline设置和定时器timer开启的操作,会放在With-系列函数中.

回顾一下: Context的deadline是机会取消信号实现的.

valueCtx

valueCtx和timerCtx不同,是直接基于Context的.

type valueCtx struct {
  Context
  key, val interface{}
}

一个valueCtx附加了一个kv对.实现了ValueString.

   func stringify(v interface{}) string {
      switch s := v.(type) {
      case stringer:
        return s.String()
      case string:
        return s
      }
      return "<not Stringer>"
    }
    func (c *valueCtx) String() string {
      return contextName(c.Context) + ".WithValue(type " +
        reflectlite.TypeOf(c.key).String() +
        ", val " + stringify(c.val) + ")"
    }
    func (c *valueCtx) Value(key interface{}) interface{} {
      if c.key == key {
        return c.val
      }
      return c.Context.Value(key)
    }

因为valueCtx.val类型是接口类型interface{},所以获取具体值时, 使用了switch type.

六、With-系列函数

支持取消信号 WithCancel:

var Canceled = errors.New("context canceled")
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,类型是cancelCtx,CancelFunc是取消操作, 具体是调用cancelCtx.cancel()函数,err参数是Canceled.

    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():
          }
        }()
      }
    }

传播取消信号.如果父Context不支持取消信号,那就不传播. 如果父Context的取消信号已经触发(就是父Context的done信道已经触发或关闭), 之后判断父Context是不是cancelCtx,如果是就将此Context丢到children中, 如果父Context不是cancelCtx,那就起协程监听父子Context的done信道.

小技巧:

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

不加default,会等到done信道有动作;加了会立马判断done信道,done没操作就结束select.

select {
case <-parent.Done():
  child.cancel(false, parent.Err())
case <-child.Done():
}

这个会等待,因为没有加default.

因为顶层Context目前只能是background和todo,不是cancelCtx, 所以顶层Context的直接派生Context不会触发propagateCancel中的和children相关操作, 至少得3代及以后才有可能.

WithCancel的取消操作会释放相关资源,所以在上下文操作完之后,最好尽快触发取消操作. 触发的方式是:done信道触发,要么有数据,要么被关闭.

支持截至日期 WithDeadline:

 

   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) {
        // The current deadline is already sooner than the new one.
        return WithCancel(parent)
      }
      c := &timerCtx{
        cancelCtx: newCancelCtx(parent),
        deadline:  d,
      }
      propagateCancel(parent, c)
      dur := time.Until(d)
      if dur <= 0 {
        c.cancel(true, DeadlineExceeded) // deadline has already passed
        return c, func() { c.cancel(false, Canceled) }
      }
      c.mu.Lock()
      defer c.mu.Unlock()
      if c.err == nil {
        c.timer = time.AfterFunc(dur, func() {
          c.cancel(true, DeadlineExceeded)
        })
      }
      return c, func() { c.cancel(true, Canceled) }
    }

With-系列函数用于生成派生Context,函数内部第一步都是判断父Context是否为nil, WithDeadline第二步是判断父Context是否支持deadline,支持就将取消信号传递给派生Context, 如果不支持,就为当前派生Context支持deadline.

先理一下思路,目前Context的实现类型有4个:emptyCtx/cancelCtx/timerCtx/valueCtx, 除了emptyCtx,实现Deadline()方法的只有timerCtx,(ps:这里的实现特指有业务意义的实现), 唯一可以构造timerCtx的只有WithDeadline的第二步中. 这么说来,顶层Context不支持deadline,最多第二层派生支持deadline的Context, 第三层派生用于将取消信号进行传播.

WithCancel上面已经分析了,派生一个支持取消信号的Context,并将父Context的取消信号 传播到派生Context(ps:这么说有点绕,简单点讲就是将派生Context添加到父Context的children), 下面看看第一个构造支持deadline的过程.

构造timerCtx,传播取消信号,判断截至日期是否已过,如果没过,利用time.AfterFunc创建定时器, 设置定时触发的协程处理,之后返回派生Context和取消函数.

可以看到,整个WithDeadline是基于WithCancel实现的,截至日期到期后,利用取消信号来做后续处理.

因为timerCtx是内嵌了cancelCtx,所以有一个派生Context是可以同时支持取消和deadline的, 后面的value支持也是如此.

WithDeadline的注释说明: 派生Context的deadline不晚于参数,如果参数晚于父Context支持的deadline,使用父Context的deadline, 如果参数指定的比父Context早,或是父Context不支持deadline,那么派生Context会构造一个新的timerCtx. 父Context的取消/派生Context的取消/或者deadline的过期,都会触发取消信号对应的操作执行, 具体就是Done()信道会被关闭.

func WithTimeout(parent Context,
  timeout time.Duration) (Context, CancelFunc) {
  return WithDeadline(parent, time.Now().Add(timeout))
}

WitchTimeout是基于WithDeadline实现的,是一种扩展,从设计上可以不加,但加了会增加调用者的便捷. WithTimeout可用在"慢操作"上.上下文使用完之后,应该立即调用取消操作来释放资源.

支持值WitchValue:

func WithValue(parent Context, key, val interface{}) 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}
}

只要是key能比较,就构造一个valueCtx,用Value()获取值时,如果和当前派生Context的key不匹配, 就会和父Context的key做匹配,如果不匹配,最后顶层Context会返回nil.

总结一下:如果是Value(),会一直通过派生Context找到顶层Context; 如果是deadline,会返回当前派生Context的deadline,但会受到父Context的deadline和取消影响; 如果是取消函数,会将传播取消信号的相关Context都做取消操作. 最重要的是Context是一个树形结构,可以组成很复杂的结构.

到目前为止,只了解了包的内部实现(顶层Context的构造/With-系列函数的派生), 具体使用,需要看例子和实际测试.

ps:一个包内部如何复杂,对外暴露一定要简洁.一个包是无法设计完美的,但是约束可以, 当大家都接受一个包,并接受使用包的规则时,这个包就成功了,context就是典型.

对于值,可以用WithValue派生,用Value取; 对于cancel/deadline,可以用WithDeadline/WithTimeout派生,通过Done信号获取结束信号, 也可以手动用取消函数来触发取消操作.整个包的功能就这么简单.

七、扩展功能以及如何扩展

扩展功能现在支持取消/deadline/value,扩展这个层级不应该放在这个包, 扩展Context,也就是新建Context的实现类型,这个是可以的, 同样实现类型需要承载扩展功能,也不合适.

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

接口canceler是保证取消信号可以在链上传播,cancel方法由cancelCtx/timerCtx实现, Done只由cancelCtx创建done信道,不管是从功能上还是方法上都没有扩展的必要.

剩下的就是Value扩展成多kv对,这个主要还是要看应用场景.

八、补充

Context被取消后Err返回Canceled错误,超时之后Err返回DeadlineExceeded错误, 这个DeadlineExceeded还有些说法: 

 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 }

再看看net.Error接口:

type Error interface {
  error
  Timeout() bool   // Is the error a timeout?
  Temporary() bool // Is the error temporary?
}

context中的DeadlineExceeded默认是实现了net.Error接口的实例. 这个是为后面走网络超时留下的扩展.

到此这篇关于对Go语言中的context包源码分析的文章就介绍到这了,更多相关Go语言context包源码分析内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: 对Go语言中的context包源码分析

本文链接: https://lsjlt.com/news/138504.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 对Go语言中的context包源码分析
    目录一、包说明分析二、包结构分析三、Context接口类型分析四、后续分析规划五、基于实现类型到常用函数六、With-系列函数七、扩展功能以及如何扩展八、补充一、包说明分析 cont...
    99+
    2024-04-02
  • Go语言context test源码分析详情
    目录1.测试例子分析2.单元测试1.测试例子分析 example_test.go,展示了With-系列的4个例子 func ExampleWithCancel() {   gen :...
    99+
    2024-04-02
  • Go中Context使用源码解析
    目录前言1、Context定义2、Context的派生2.1、创建Context对象2.2、parent Context3、context 接口四种实现4、 emptyCtx 源码分...
    99+
    2023-05-16
    Go Context源码解析 Go Context
  • Go语言进阶freecache源码分析
    这篇文章主要介绍“Go语言进阶freecache源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言进阶freecache源码分析”文章能帮助大家解决问题。00. 什么是 freecach...
    99+
    2023-07-06
  • Go语言读写锁RWMutex的源码分析
    目录前言RWMutex 总览深入源码数据结构RLock()RUnlock()Lock()Unlock()常见问题实战一下前言 在前面两篇文章中 初见 Go Mutex&nb...
    99+
    2024-04-02
  • Go语言io pipe源码分析详情
    目录1.结构分析2.pipe sruct分析3.PipeReader对外暴露的是读/关闭4.写法5.总结pipe.go分析: 这个文件使用到了errors包,也是用到了sync库.文...
    99+
    2024-04-02
  • go语言构建顺序源码分析
    这篇文章主要介绍“go语言构建顺序源码分析”,在日常操作中,相信很多人在go语言构建顺序源码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”go语言构建顺序源码分析”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-07-05
  • Go语言中的闭包实例分析
    这篇文章主要介绍“Go语言中的闭包实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言中的闭包实例分析”文章能帮助大家解决问题。一、函数的变量作用域和可见性全局变量在main函数执行之前初...
    99+
    2023-07-02
  • 如何分析Go语言的库源码文件
    这期内容当中小编将会给大家带来有关如何分析Go语言的库源码文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。go适合做什么go是golang...
    99+
    2024-04-02
  • 怎么进行Go语言HTTP Server源码分析
    这期内容当中小编将会给大家带来有关怎么进行Go语言HTTP Server源码分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Go语言中HTTP Server:HTTP  server,顾名思义...
    99+
    2023-06-17
  • Go语言网络编程与Http源码分析
    这篇文章主要介绍“Go语言网络编程与Http源码分析”,在日常操作中,相信很多人在Go语言网络编程与Http源码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Go语言网络编程与Http源码分析”的疑惑有所...
    99+
    2023-07-05
  • Go语言中基础闭包的示例分析
    这篇文章将为大家详细讲解有关Go语言中基础闭包的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一. 闭包概述闭包就是解决局部变量不能被外部访问的一种解决方案闭包是把函数当作返回值的一种应用二. 代...
    99+
    2023-06-25
  • Go语言学习之context包的用法详解
    目录前言需求一需求二Context 接口emptyCtxvalueCtx类型定义WithValuecancelCtx类型定义cancelCtxWithCanceltimerCtx类型...
    99+
    2024-04-02
  • go语言字符类型byte与rune源码分析
    这篇文章主要介绍“go语言字符类型byte与rune源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“go语言字符类型byte与rune源码分析”文章能帮助大家解决问题。一、byte类型介绍by...
    99+
    2023-07-05
  • 二维码对象开发:Go语言和Apache的对比分析?
    二维码是一种常见的图像编码方式,它可以将大量的信息编码成一个小小的图像,方便在不同的场景中使用。在实际应用中,我们常常需要开发二维码对象,用于生成、解析和处理二维码。而在二维码对象的开发中,Go语言和Apache是两种常见的选择。本文将对G...
    99+
    2023-08-31
    二维码 apache 对象
  • Go语言中协程和线程的对比分析
    Go语言协程(Goroutine)与线程(Thread)是并发编程中常见的两种概念,它们都可以用来处理并发任务,但在实现方式、调度方式、资源消耗等方面有着显著的不同。本文将深入探讨Go...
    99+
    2024-02-25
    go语言 线程 协程
  • react中context传值和生命周期源码分析
    本篇内容主要讲解“react中context传值和生命周期源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“react中context传值和生命周期源码分析”吧!假设:项目中存在复杂组件树:...
    99+
    2023-07-05
  • go语言context包功能及操作使用的方法
    本篇内容介绍了“go语言context包功能及操作使用的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Context包到底是干嘛用的?我...
    99+
    2023-06-30
  • 浅析Go语言中闭包的使用
    目录闭包基本介绍闭包实现数字累加代码说明代码分析闭包案例上代码代码说明闭包基本介绍 闭包就是 一个函数 和其相关的 引用环境 组合的一个整体 ...
    99+
    2022-12-08
    Go语言闭包使用 Go语言闭包 Go 闭包
  • PHP与Go语言的优劣对比分析
    PHP与Go语言是两种常用的编程语言,它们各有优劣。PHP是一种脚本语言,特别适合用于Web开发,而Go语言则是一种静态类型的编译型语言,被广泛应用于云平台和大规模分布式系统。本文将对...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作