世界上最伟大的投资就是投资自己的教育

首页Golang
随风 · 练气

前端程序员学习 Golang gin 框架实战笔记之二分析 context

随风发布于308 次阅读

上一节: 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin

之前讲到了如何使用 gin,这一节我们来分析和调试一下它的代码。

New()

第一行的 gin.New() ,其实还有一种写法:

gin.Default()

有什么区别呢?

你很容易查看:

你的鼠标在 New 方法停留,然后会弹出如下的窗口:

这个编辑器会告诉你这个方法的意义。

就是返回一个没有带中间件的 gin 实例。

关于中间件以后再来讲。

那么你可以点进去看 New 方法,苹果系统,按住键盘上的 command,再单击就可以进去:

进去后,可以查看它源码:

*Engine 这个是它的返回值,是个指针。

golang 也是有指针的,有点仿照 c。

关于指针,可以运行下面的地址看看:

package main

import "fmt"

func main() {

    var count int = 4
    fmt.Println(count)

    var pv = &count
    *pv = 3
    fmt.Println(pv)
    fmt.Println(*pv)

    var pv2 *int = &count
    *pv = 2
    fmt.Println(pv2)
    fmt.Println(*pv2)

    pv3 := &count
    *pv = 1
    fmt.Println(pv3)
    fmt.Println(*pv3)
}

我的理解就是指向值或对象的内存地址,如果要改对象或结构体内容,就传指针。

我们传结构体的时候,你不用指针,会把值传过去,可能相当于 copy 一份数据传过去,那么无法对结构体修改,只有传指针才行。

你在这个文件搜索一下 Default 方法:

我们可以比较一下,就是调了上面的 New 方法之外,多加了两个中间件:

engine.Use(Logger(), Recovery())

一个日志用的,另一个是来恢复程序用的。

Context

我们再来看下这个 Context 是个啥。

你可以在网上找到它的概念,其他语言或框架也有,只是跟 Golang 的有些区别。

我对它的理解不是很深,先说下:

它首先是个结构体:

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

我看别人对它的解释是这样的:

context 是一个带有截止时间,取消信号和 key,value 的上下文对象。

首先它有值。

到底体现在哪呢?

我们来调试一下:

先退出之前的 go run main.go 进程,用 control + c 即可。

我这里用 GoLang 编辑器:

我们用 Debug 模式来运行。

然后你用 postman 访问一下:

这个 Context 有啥值呀,就是有参数,请求这些东西,路径啥的,比如可以从 postman 得到请求的参数。

举例

还有个例子是这样的:

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    go handle(ctx, 500*time.Millisecond)
    select {
    case <-ctx.Done():
        fmt.Println("main", ctx.Err())
    }
}

func handle(ctx context.Context, duration time.Duration) {
    select {
    case <-ctx.Done():
        fmt.Println("handle", ctx.Err())
    case <-time.After(duration):
        fmt.Println("process request with", duration)
    }
}

你执行下,看它会输出啥:

我们在写协程,或其他语言,写线程进程的时候,我们这个主协程要对这个协程的状态进行控制,不然你主协程都退出了,协程还在运行就不太好。

或者我们要共享或传一些变量,其他语言可能会用共享内存,队列,信号量啥的。

这里我们就可以传一个 Context 过去。

handle 协程中,我们通过 ctx.Done() 判断主程是不是执行完成,那主程要一秒后才完成,协程才 500 ms,最后肯定是先输出

fmt.Println("process request with", duration)

handle 方法就结束了,再过了 500 ms, 就输出:

fmt.Println("main", ctx.Err())

整个世界就结束了。

在协程中是可以知道主协的运行状态的,至少能知道是不是结束了。

你不用 context 的话,可能就像下面这样:

func main() {
    fmt.Println("main is start")
    go Test("value")
    fmt.Println("main is finish")
    time.Sleep(3 * time.Second)
}

func Test(value string) {
    fmt.Println("test is start")
    time.Sleep(5 * time.Second)
    fmt.Println("test is finish")
}

你执行你的,我执行我的,无法获得主程和协程的状态。

这个叫 “同步信号”。

我们还可以传值:

func main() {
    fmt.Println("main is start")
    ctx := context.WithValue(context.Background(), "key", "value")
    go TestValue(ctx)
    time.Sleep(1 * time.Second)
}

func TestValue(ctx context.Context) {
    fmt.Println("=======:", ctx.Value("key"))
    select {
    case <-ctx.Done():
        fmt.Println("[TestContext] and value is ", ctx.Value("key"))
    }
}

使用 context.WithValue 就可以把值传给协程。

这样就共享数据了呀。

关于 Background 方法 ,可以看:

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
    return background
}

// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).
func TODO() Context {
    return todo
}

它返回非空的 context, 主要用于主线程来用。

网上有大佬是这样解释的:

本站文章均为原创内容,如需转载请注明出处,谢谢。

0 条回复
暂无回复~~
喜欢
统计信息
    学员: 25884
    视频数量: 1743
    文章数量: 486

© 汕尾市求知科技有限公司 | Rails365 Gitlab | Qiuzhi99 Gitlab | 知乎 | b 站 | 搜索

粤公网安备 44152102000088号粤公网安备 44152102000088号 | 粤ICP备19038915号

Top