golang学习笔记之-context详细理解篇

栏目: Go · 发布时间: 6年前

内容简介:2.context.WithDeadline()和context.WithTimeout():返回Context和取消函数用来取消Context(这个取消函数会根据设置的时间自动取消),5.遇到某个上下文在传递的时候:context.WithCancel()存储和检索附加于请求的数据包.当一个函数创建一个goroutine和Context时,它通常会启动一个为请求提供服务的进程,并且子函数可能需要相关的请求信息。使用的key必须是可比较的,也就是说== 和 != 必须能返回正确的结果
golang学习笔记之-context详细理解篇

image.png

  • context.Background():可以简单理解我们知道这个上下文要去干什么
  • context.TODO():可以简单理解我们不清楚要使用哪个上下文、或者还没有可用的上下文

下面代码演示:

1.context.WithCancel():返回Context和取消函数用来取消Context

package main

import (
    "context"
    "log"
    "os"
    "time"
)

var (
    logg *log.Logger
)

func work(ctx context.Context, ch chan bool) {
    for {
        select {
        case <-ctx.Done():
            logg.Println(`下班!`)
            ch <- true
            return
        default:
            logg.Println(`上班!`)
            time.Sleep(2 * time.Second)
        }
    }
}

func main() {
    ch := make(chan bool)
    logg = log.New(os.Stdout, "", log.Ltime)
    ctx, cancel := context.WithCancel(context.Background())
    go work(ctx, ch)
    time.Sleep(10 * time.Second)
    //取消函数:当cancel被调用时,WithCancel遍历Done以执行关闭;
    cancel()
    // 这个chan是为了保证子的goroutine执行完,当然也可以不用chan用time.Sleep停止几秒
    <-ch
    logg.Println(`无脑发呆中!`)
}
/* outfile:
17:27:52 上班!
17:27:54 上班!
17:27:56 上班!
17:27:58 上班!
17:28:00 上班!
17:28:02 下班!
17:28:02 无脑发呆中!
*/

2.context.WithDeadline()和context.WithTimeout():返回Context和取消函数用来取消Context(这个取消函数会根据设置的时间自动取消),

package main

import (
    "context"
    "log"
    "os"
    "time"
)

var (
    logg *log.Logger
)

func work(ctx context.Context, ch chan bool) {
    for {
        select {
        case <-ctx.Done():
            logg.Println(`下班!`)
            ch <- true
            return
        default:
            logg.Println(`上班!`)
            time.Sleep(2 * time.Second)
        }
    }
}

func main() {
    ch := make(chan bool)
    logg = log.New(os.Stdout, "", log.Ltime)
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
    go work(ctx, ch)
    time.Sleep(10 * time.Second)
    //取消函数:当cancel被调用时,context.WithDeadline设置的时间超过了,关闭ctx.Done通道。
    cancel()
    // 这个chan是为了保证子的goroutine执行完,当然也可以不用chan用time.Sleep停止几秒
    <-ch
    logg.Println(`无脑发呆中!`)
}
/* outfile:
17:29:43 上班!
17:29:45 上班!
17:29:47 上班!
17:29:49 下班!
17:29:53 无脑发呆中!
*/

3.context.WithTimeout

package main

import (
    "context"
    "log"
    "os"
    "time"
)

var (
    logg *log.Logger
)

func work(ctx context.Context, ch chan bool) {
    for {
        select {
        case <-ctx.Done():
            logg.Println(`下班!`)
            ch <- true
            return
        default:
            logg.Println(`上班!`)
            time.Sleep(2 * time.Second)
        }
    }
}

func main() {
    ch := make(chan bool)
    logg = log.New(os.Stdout, "", log.Ltime)
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    go work(ctx, ch)
    time.Sleep(10 * time.Second)
    //取消函数:当cancel被调用时,context.WithTimeout设置的时间超过后,关闭ctx.Done通道;
    cancel()
    // 这个chan是为了保证子的goroutine执行完,当然也可以不用chan用time.Sleep停止几秒
    <-ch
    logg.Println(`无脑发呆中!`)
}
/*  outfile:
17:34:56 上班!
17:34:58 上班!
17:35:00 上班!
17:35:02 下班!
17:35:06 无脑发呆中!
*/
  • context.WithCancel():执行取消函数就取消
  • context.WithDeadline、context.WithTimeout:超时的时候就取消

4.Deadline获取超时时间

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "time"
)

var (
    logg *log.Logger
)

func work(ctx context.Context, ch chan bool) {
    for {
        /*
        Deadline:是获取设置的超时时间:
        第一个返回值:设置的超时时间,到超时时间Context会自动发起取消请求
        第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话需要调用取消函数进行取消。
        */
        if deadline, ok := ctx.Deadline(); ok {
            fmt.Println(deadline)
            if time.Now().After(deadline) {
                logg.Println(`超时退出!`)
                //这里是为了演示,Context中的Err()输出:context deadline exceeded
                logg.Printf(ctx.Err().Error())
                ch <- true
                return
            }

        }
        select {
        case <-ctx.Done():
            logg.Println(`下班!`)
            ch <- true
            return
        default:
            logg.Println(`上班!`)
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ch := make(chan bool)
    logg = log.New(os.Stdout, "", log.Ltime)
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    go work(ctx, ch)
    time.Sleep(10 * time.Second)
    //取消函数:当cancel被调用时,context.WithTimeout设置的时间超过后,关闭ctx.Done通道;
    cancel()
    // 这个chan是为了保证子的goroutine执行完,当然也可以不用chan用time.Sleep停止几秒
    <-ch
    logg.Println(`无脑发呆中!`)
}
/* outfile:
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:42 上班!
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:43 上班!
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:44 上班!
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:45 上班!
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:46 上班!
2019-01-30 18:23:47.851042 +0800 CST m=+5.000360703
18:23:47 超时退出!
18:23:47 context deadline exceeded //这里就是ctx超时的时候ctx.Err的错误消息。
18:23:52 无脑发呆中!
*/

5.遇到某个上下文在传递的时候:context.WithCancel()存储和检索附加于请求的数据包.当一个函数创建一个goroutine和Context时,它通常会启动一个为请求提供服务的进程,并且子函数可能需要相关的请求信息。

package main

import (
    "context"
    "fmt"
)

func main() {
    ProcessRequest("admin", "admin888")
}

func ProcessRequest(UserName, PassWord string) {
    ctx := context.WithValue(context.Background(), "UserName", UserName)
    ctx = context.WithValue(ctx, "PassWord", PassWord)
    HandleResponse(ctx)
}

func HandleResponse(ctx context.Context) {
    fmt.Printf("处理响应 用户名:%v 密码:%v",
        ctx.Value("UserName"),
        ctx.Value("PassWord"),
    )
}
/* outfile:
处理响应 用户名:admin 密码:admin888
*/
  • 很简单的用法,不过也是有限制的:

使用的key必须是可比较的,也就是说== 和 != 必须能返回正确的结果

返回值必须是并发安全的,这样才能从多个goroutine访问

  • 由于Context的Value(key interface{}) interface{} 键和值都被定义为interface{},当试图检索值时,会失去其类型安全性。基于此,Go建议在context中存储和检索值时遵循一些规则。
  • 推荐在包中自行定义key的类型,这样无论是否其他包执行相同的操作都可以防止context中的冲突。看下面这个例子:
type foo int
type bar int

m := make(map[interface{}]int)
m[foo(1)] = 1
m[bar(1)] = 2

fmt.Printf("%v", m)
/*
map[1:2 1:1]
*/
  • 可以看到,虽然基础值是相同的,但不同类型的信息会在map中区分它们。由于你为包定义的key类型未导出,因此其他包不会与你在包中生成的key冲突。
  • 由于用于存储数据的key是非导出的,因此我们必须导出执行检索数据的函数。这很容易做到,因为它允许这些数据的使用者使用静态的,类型安全的函数。

当你把所有这些放在一起时,你会得到类似下面的例子:

package main

import (
    "context"
    "fmt"
)

func main() {
    ProcessRequest("jane", "abc123")
}

type ctxKey int

const (
    ctxUserName ctxKey = iota
    ctxPassWord
)

func UserName(c context.Context) string {
    return c.Value(ctxUserName).(string)
}

func PassWord(c context.Context) string {
    return c.Value(ctxPassWord).(string)
}

func ProcessRequest(UserName, PassWord string) {
    ctx := context.WithValue(context.Background(), ctxUserName, UserName)
    ctx = context.WithValue(ctx, ctxPassWord, PassWord)
    HandleResponse(ctx)
}

func HandleResponse(ctx context.Context) {
    fmt.Printf(
        "处理响应 用户名:%v 密码:%v",
        UserName(ctx),
        PassWord(ctx),
    )
}
/*  outfile:
处理响应 用户名:jane 密码:abc123
*/

以上所述就是小编给大家介绍的《golang学习笔记之-context详细理解篇》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cracking the Coding Interview

Cracking the Coding Interview

Gayle Laakmann McDowell / CareerCup / 2015-7-1 / USD 39.95

Cracking the Coding Interview, 6th Edition is here to help you through this process, teaching you what you need to know and enabling you to perform at your very best. I've coached and interviewed hund......一起来看看 《Cracking the Coding Interview》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试