背景

在go服务器中,对于每个请求的request都是在单独的goroutine中进行的,处理一个request也可能设计多个goroutine之间的交互, 使用context可以使开发者方便的在这些goroutine里传递request相关的数据、取消goroutine的signal或截止日期。

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{}
}

Done 方法在Context被取消或超时时返回一个close的channel,close的channel可以作为广播通知,告诉给context相关的函数要停止当前工作然后返回。

当一个父operation启动一个goroutine用于子operation,这些子operation不能够取消父operation。下面描述的WithCancel函数提供一种方式可以取消新创建的Context.

Context可以安全的被多个goroutine使用。开发者可以把一个Context传递给任意多个goroutine然后cancel这个context的时候就能够通知到所有的goroutine。

Err方法返回context为什么被取消。

Deadline返回context何时会超时。

Value返回context相关的数据。

继承的Context

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

BackGound是所有Context的root,不能够被cancel。

WithCancel

WithCancel返回一个继承的Context,这个Context在父Context的Done被关闭时关闭自己的Done通道,或者在自己被Cancel的时候关闭自己的Done。
WithCancel同时还返回一个取消函数cancel,这个cancel用于取消当前的Context。

package main

import (
"context"
"log"
"os"
"time"
) var logg *log.Logger func someHandler() {
ctx, cancel := context.WithCancel(context.Background())
go doStuff(ctx) //10秒后取消doStuff
time.Sleep( * time.Second)
cancel() } //每1秒work一下,同时会判断ctx是否被取消了,如果是就退出
func doStuff(ctx context.Context) {
for {
time.Sleep( * time.Second)
select {
case <-ctx.Done():
logg.Printf("done")
return
default:
logg.Printf("work")
}
}
} func main() {
logg = log.New(os.Stdout, "", log.Ltime)
someHandler()
logg.Printf("down")
}

返回:

E:\wdy\goproject>go run context_learn.go
:: work
:: work
:: work
:: work
:: work
:: work
:: work
:: work
:: work
:: down

withDeadline withTimeout

WithTimeout func(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).

WithTimeout 等价于 WithDeadline(parent, time.Now().Add(timeout)).

对上面的样例代码进行修改

func timeoutHandler() {
// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(*time.Second))
// go doTimeOutStuff(ctx)
go doStuff(ctx) time.Sleep( * time.Second) cancel() } func main() {
logg = log.New(os.Stdout, "", log.Ltime)
timeoutHandler()
logg.Printf("end")
}

返回:

:: work
:: work
:: work
:: work
:: done
:: end

可以看到doStuff在context超时的时候被取消了,ctx.Done()被关闭。
将context.WithDeadline替换为context.WithTimeout

func timeoutHandler() {
ctx, cancel := context.WithTimeout(context.Background(), *time.Second)
// ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
// go doTimeOutStuff(ctx)
go doStuff(ctx) time.Sleep( * time.Second) cancel() }
:: work
:: work
:: work
:: work
:: done
:: end

doTimeOutStuff替换doStuff

func doTimeOutStuff(ctx context.Context) {
for {
time.Sleep( * time.Second) if deadline, ok := ctx.Deadline(); ok { //设置了deadl
logg.Printf("deadline set")
if time.Now().After(deadline) {
logg.Printf(ctx.Err().Error())
return
} } select {
case <-ctx.Done():
logg.Printf("done")
return
default:
logg.Printf("work")
}
}
} func timeoutHandler() {
ctx, cancel := context.WithTimeout(context.Background(), *time.Second)
// ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
go doTimeOutStuff(ctx)
// go doStuff(ctx) time.Sleep( * time.Second) cancel() }
:: deadline set
:: work
:: deadline set
:: work
:: deadline set
:: work
:: deadline set
:: work
:: deadline set
:: context deadline exceeded
:: end
WithTimeout
package main

import (
"math/rand"
"time"
"sync"
"fmt"
"context"
) func main() {
rand.Seed(time.Now().Unix()) ctx,_:=context.WithTimeout(context.Background(),time.Second*) var wg sync.WaitGroup
wg.Add()
go GenUsers(ctx,&wg)
wg.Wait() fmt.Println("生成幸运用户成功")
}
func GenUsers(ctx context.Context,wg *sync.WaitGroup) { //生成用户ID
fmt.Println("开始生成幸运用户")
users:=make([]int,)
guser:for{
select{
case <- ctx.Done(): //代表父context发起 取消操作 fmt.Println(users)
wg.Done()
break guser
return
default:
users=append(users,getUserID(,))
}
} }
func getUserID(min int ,max int) int {
return rand.Intn(max-min)+min
}

context deadline exceeded就是ctx超时的时候ctx.Err的错误消息。

搜索测试程序

完整代码参见官方文档Go Concurrency Patterns: Context,其中关键的地方在于函数httpDo

func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
tr := &http.Transport{}
client := &http.Client{Transport: tr}
c := make(chan error, )
go func() { c <- f(client.Do(req)) }()
select {
case <-ctx.Done():
tr.CancelRequest(req)
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}

httpDo关键的地方在于

 select {
case <-ctx.Done():
tr.CancelRequest(req)
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}

要么ctx被取消,要么request请求出错。

httpserver中实现超时

package main

import (
"net/http"
"context"
"time"
) func CountData(c chan string) chan string {
time.Sleep(time.Second*)
c<- "统计结果"
return c
} type IndexHandler struct {}
func(this *IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("count")==""{
w.Write([]byte("这是首页"))
}else {
ctx,cancel:=context.WithTimeout(r.Context(),time.Second*)
defer cancel()
c:=make(chan string)
go CountData(c)
select {
case <-ctx.Done():
w.Write([]byte("超时"))
case ret:=<-c:
w.Write([]byte(ret))
} } } func main() {
mux:=http.NewServeMux()
mux.Handle("/",new(IndexHandler)) http.ListenAndServe(":8082",mux)
}

WithValue

func WithValue(parent Context, key interface{}, val interface{}) Context
// NewContext returns a new Context carrying userIP.
func NewContext(ctx context.Context, userIP net.IP) context.Context {
return context.WithValue(ctx, userIPKey, userIP)
} // FromContext extracts the user IP address from ctx, if present.
func FromContext(ctx context.Context) (net.IP, bool) {
// ctx.Value returns nil if ctx has no value for the key;
// the net.IP type assertion returns ok=false for nil.
userIP, ok := ctx.Value(userIPKey).(net.IP)
return userIP, ok
}

golang context用法详解的更多相关文章

  1. Golang Context 包详解

    Golang Context 包详解 0. 引言 在 Go 语言编写的服务器程序中,服务器通常要为每个 HTTP 请求创建一个 goroutine 以并发地处理业务.同时,这个 goroutine 也 ...

  2. 【GoLang】golang context channel 详解

    代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...

  3. golang格式化输出-fmt包用法详解

    golang格式化输出-fmt包用法详解 注意:我在这里给出golang查询关于包的使用的地址:https://godoc.org    声明: 此片文章并非原创,大多数内容都是来自:https:// ...

  4. linux管道命令grep命令参数及用法详解---附使用案例|grep

    功能说明:查找文件里符合条件的字符串. 语 法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>] ...

  5. 【Ext.Net学习笔记】03:Ext.Net DirectEvents用法详解、DirectMethods用法详解

    Ext.Net通过DirectEvents进行服务器端异步的事件处理.[Ext.Net学习笔记]02:Ext.Net用法概览.Ext.Net MessageBus用法.Ext.Net布局 中已经简单的 ...

  6. Android GLSurfaceView用法详解(二)

    输入如何处理       若是开发一个交互型的应用(如游戏),通常需要子类化 GLSurfaceView,由此可以获取输入事件.下面有个例子: java代码: package eoe.ClearTes ...

  7. Ext.Net学习笔记22:Ext.Net Tree 用法详解

    Ext.Net学习笔记22:Ext.Net Tree 用法详解 上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat=&q ...

  8. 转载 LayoutInflater的inflate函数用法详解

    http://www.open-open.com/lib/view/open1328837587484.html LayoutInflater的inflate函数用法详解 LayoutInflater ...

  9. grep用法详解:grep与正则表达式【转】

    转自:http://blog.csdn.net/hellochenlian/article/details/34088179 grep用法详解:grep与正则表达式 首先要记住的是: 正则表达式与通配 ...

随机推荐

  1. Django项目: 项目环境搭建 ---- 三、在码云平台创建项目&推送到码云上

    三.在码云平台创建项目 git服务平台: 主要使用github(最主流) 国内访问速度慢 托管私有项目收费 国内一般使用码云gitee 国内访问速度快 托管私有项目免费(限制开发人数) 公司中使用gi ...

  2. Angular+Bootstrap3导航菜单

    Angular+Bootstrap3导航菜单 AngularJS体验式编程系列文章,将介绍如何用angularjs构建一个强大的web前端系统.angularjs是由Google团队开发的一款非常优秀 ...

  3. 2、mysql密码过期的修改方法(your password has expired)

    今天打开SQLyog提示密码过期:Your password has expired 解决方法:    1.  启动MySQL服务 2.  启动MySQL后台 3.  执行以下命令 step 1: S ...

  4. Lucene 评分机制一

    1. 评分公式 1.1 公式介绍 这个公式是Lucene实际计算时使用的公式,是由原型公式推导而来 tf(t in d) 表示某个term的出现频率,定义了term t出现在当前document d的 ...

  5. LUOGU P4113 [HEOI2012]采花

    传送门 解题思路 莫队题卡莫队...莫队只能拿到100分,满分200.正解主席树??发个莫队100分代码. 代码 #include<iostream> #include<cstdio ...

  6. Vue简单评星效果与单张图片上传

    <form class="" id="pj-frm"> <div class="assess-header"> &l ...

  7. Node.js学习(Node.js基础)

    1.开发环境搭建 v8引擎是作为解析JavaScript程序来运行的 nodejs是一个高性能的,第一个体现在他的JavaScript解析速度很快,v8引擎性能很高,第二个事件驱动和非阻塞 2.全局对 ...

  8. 关于apache kylin 安装32位linux办法

    最近公司在使用apache kylin做实时开发访问,但是自己集群是linux32 只能安装JDK32位的受限只能3G多内存,而apachekylin 默认是4G内存,需要JDK64位支持. 解决办法 ...

  9. Luogu P3254 圆桌问题(最大流)

    P3254 圆桌问题 题面 题目描述 假设有来自 \(m\) 个不同单位的代表参加一次国际会议.每个单位的代表数分别为 \(r_i (i =1,2,--,m)\) . 会议餐厅共有 \(n\) 张餐桌 ...

  10. Leetcode506.Relative Ranks相对名次

    给出 N 名运动员的成绩,找出他们的相对名次并授予前三名对应的奖牌.前三名运动员将会被分别授予 "金牌","银牌" 和" 铜牌"(" ...