Golang面试前二夜准备

简介: Golang面试前二夜准备

640.png

Golang面试前二夜准备



题号 题目
24 Context包的用途是什么
25 Go主协程如何等其余协程完再操作


24. Context包的用途是什么


在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和RPC服务。用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源。


在Google 内部,我们开发了 Context 包,专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。

context的数据结构是:


// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this `Context` is canceled
    // or times out.
    Done() <-chan struct{}
    // Err indicates why this Context was canceled, after the Done channel
    // is closed.
    Err() error
    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)
    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}


Context中的方法:


  • Done会返回一个channel,当该context被取消的时候,该channel会被关闭,同时对应的使用该context的routine也应该结束并返回。
  • Context中的方法是协程安全的,这也就代表了在父routine中创建的context,可以传递给任意数量的routine并让他们同时访问。
  • Deadline会返回一个超时时间,routine获得了超时时间后,可以对某些io操作设定超时时间。
  • Value可以让routine共享一些数据,当然获得数据是协程安全的。


这里需要注意一点的是在goroutine中使用context包的时候,通常我们需要在goroutine中新创建一个上下文的context,原因是:如果直接传递外部context到协层中,一个请求可能在主函数中已经结束,在goroutine中如果还没有结束的话,会直接导致goroutine中的运行的被取消.


go func() {
   _, ctx, _ := log.FromContextOrNew(context.Background(), nil)
}()


context.Background函数的返回值是一个空的context,经常作为树的根结点,它一般由接收请求的第一个routine创建,不能被取消、没有值、也没有过期时间。


Background函数的声明如下:


// Background returns an empty Context. It is never canceled, has no deadline,
// and has no values. Background is typically used in main, init, and tests,
// and as the top-level `Context` for incoming requests.
func Background() Context


WithCancel 和 WithTimeout 函数 会返回继承的 Context 对象, 这些对象可以比它们的父 Context 更早地取消。


当请求处理函数返回时,与该请求关联的 Context 会被取消。当使用多个副本发送请求时,可以使用 WithCancel取消多余的请求。WithTimeout 在设置对后端服务器请求截止时间时非常有用。下面是这三个函数的声明:


// WithCancel returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed or cancel is called.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// A CancelFunc cancels a Context.
type CancelFunc func()
// WithTimeout returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed, cancel is called, or timeout elapses. The new
// Context's Deadline is the sooner of now+timeout and the parent's deadline, if
// any. If the timer is still running, the cancel function releases its
// resources.
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)


调用CancelFunc对象将撤销对应的Context对象,这样父结点的所在的环境中,获得了撤销子节点context的权利,当触发某些条件时,可以调用CancelFunc对象来终止子结点树的所有routine。在子节点的routine中,需要判断何时退出routine:


select {
    case <-cxt.Done():
        // do some cleaning and return
}


根据cxt.Done()判断是否结束。当顶层的Request请求处理结束,或者外部取消了这次请求,就可以cancel掉顶层context,从而使整个请求的routine树得以退出。


WithDeadline和WithTimeout比WithCancel多了一个时间参数,它指示context存活的最长时间。如果超过了过期时间,会自动撤销它的子context。所以context的生命期是由父context的routine和deadline共同决定的。


WithValue 函数能够将请求作用域的数据与 Context 对象建立关系。声明如下:


type valueCtx struct {
    Context
    key, val interface{}
}
func WithValue(parent Context, key, val interface{}) Context {
    if key == nil {
        panic("nil key")
    }
    ......
    return &valueCtx{parent, key, val}
}
func (c *valueCtx) Value(key interface{}) interface{} {
    if c.key == key {
        return c.val
    }
    return c.Context.Value(key)
}


WithValue返回parent的一个副本,该副本保存了传入的key/value,而调用Context接口的Value(key)方法就可以得到val。注意在同一个context中设置key/value,若key相同,值会被覆盖。


Context上下文数据的存储就像一个树,每个结点只存储一个key/value对。WithValue()保存一个key/value对,它将父context嵌入到新的子context,并在节点中保存了key/value数据。Value()查询key对应的value数据,会从当前context中查询,如果查不到,会递归查询父context中的数据。


值得注意的是,context中的上下文数据并不是全局的,它只查询本节点及父节点们的数据,不能查询兄弟节点的数据。


Context 使用原则:

  • 不要把Context放在结构体中,要以参数的方式传递。
  • 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
  • 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO。
  • Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递。
  • Context是线程安全的,可以放心的在多个goroutine中传递。


25. Go主协程如何等其余协程完再操作


Go提供了更简单的方法——使用sync.WaitGroup。WaitGroup,就是用来等待一组操作完成的。WaitGroup内部实现了一个计数器,用来记录未完成的操作个数.


它提供了三个方法,Add()用来添加计数。Done()用来在操作结束时调用,使计数减一。Wait()用来等待所有的操作结束,即计数变为0,该函数会在计数不为0时等待,在计数为0时立即返回。


应用示例:

package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg sync.WaitGroup
    wg.Add(2) // 因为有两个动作,所以增加2个计数
    go func() {
        fmt.Println("Goroutine 1")
        wg.Done() // 操作完成,减少一个计数
    }()
    go func() {
        fmt.Println("Goroutine 2")
        wg.Done() // 操作完成,减少一个计数
    }()
    wg.Wait() // 等待,直到计数为0
}


运行输出:

Goroutine 2
Goroutine 1


相关文章
|
3月前
|
存储 安全 算法
golang面试:golang实现原理(二)
golang面试:golang实现原理(二)
34 0
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello(&quot;Alice&quot;)`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
13 1
|
3月前
|
监控 编译器 Linux
golang面试:golang的GPM调度模型(七)
golang面试:golang的GPM调度模型(七)
31 1
|
3月前
|
监控 安全 Go
golang面试:golang中的context(四)
golang面试:golang中的context(四)
44 0
|
3月前
|
安全 Linux Go
golang面试:golang并发与多线程(三)
golang面试:golang并发与多线程(三)
45 0
|
3月前
|
JSON 安全 Java
golang面试:golang基础语法(一)
golang面试:golang基础语法(一)
41 0
|
9月前
|
缓存 并行计算 算法
Golang面试前三夜准备:Goroutine调度器GMP为何需要P?不需要会怎样?
Golang面试前三夜准备:Goroutine调度器GMP为何需要P?不需要会怎样?
|
30天前
|
Java 程序员
java线程池讲解面试
java线程池讲解面试
53 1
|
2月前
|
存储 关系型数据库 MySQL
2024年Java秋招面试必看的 | MySQL调优面试题
随着系统用户量的不断增加,MySQL 索引的重要性不言而喻,对于后端工程师,只有在了解索引及其优化的规则,并应用于实际工作中后,才能不断的提升系统性能,开发出高性能、高并发和高可用的系统。 今天小编首先会跟大家分享一下MySQL 索引中的各种概念,然后介绍优化索引的若干条规则,最后利用这些规则,针对面试中常考的知识点,做详细的实例分析。
252 0
2024年Java秋招面试必看的 | MySQL调优面试题
|
2月前
|
存储 算法 Java
铁子,你还记得这些吗----Java基础【拓展面试常问题型】
铁子,你还记得这些吗----Java基础【拓展面试常问题型】
47 1