一文带你深入理解GolangContext包_第1页
一文带你深入理解GolangContext包_第2页
一文带你深入理解GolangContext包_第3页
一文带你深入理解GolangContext包_第4页
一文带你深入理解GolangContext包_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第一文带你深入理解GolangContext包目录1.基本原理1.1Context包的介绍1.2Context的创建1.2.1WithCancel1.2.2WithDeadline1.2.3WithTimeout1.2.4WithValue2.Context的使用场景2.1并发控制2.2超时控制2.3数据库连接2.4HTTP请求2.5gRPC请求3.总结

1.基本原理

1.1Context包的介绍

在Go语言中,Context包是用于传递请求范围数据、取消信号和截止时间的机制。它通常被用来处理goroutine之间的通信和取消。Context包是Go语言内置的,它可以很方便地使用,而不需要额外的依赖。

Context包是一个轻量级的工具,它提供了一个标准的接口,用于在goroutine之间传递请求范围的数据、取消信号和截止时间。Context包实现了一种类似于树状结构的数据结构,其中每个节点都表示一个请求范围。每个节点都有一个唯一的key-value对,其中key是一个interface{}类型的值,而value则是任何类型的值。Context包还提供了一个可选的超时机制,用于在一定时间后自动取消请求。

Context包的核心是一个Context接口,它定义了一些方法,用于获取请求范围数据、取消请求和处理超时。

typeContextinterface{

Deadline()(deadlinetime.Time,okbool)

Done()-chanstruct{}

Err()error

Value(keyinterface{})interface{}

}

Deadline()方法返回截止时间和一个布尔值,指示截止时间是否已经设置。Done()方法返回一个只读的channel,当请求被取消或超时时,该channel将被关闭。Err()方法返回一个错误,指示为什么请求被取消。Value()方法返回与给定key相关联的值,如果没有值,则返回nil。

Context包还提供了两个用于创建Context的函数:WithContext和Background。Background函数返回一个空的Context,而WithContext函数则根据给定的父Context创建一个新的Context。

Context包的基本原理是通过在goroutine之间传递Context来实现请求范围数据、取消信号和截止时间的管理。当一个goroutine创建了一个新的goroutine时,它将Context作为参数传递给新的goroutine。新的goroutine可以使用这个Context来访问请求范围数据、接收取消信号和处理超时。

1.2Context的创建

在Golang中,Context可以通过WithCancel、WithDeadline、WithTimeout和WithValue等函数来创建。下面分别介绍这些函数的用法和注意事项。

1.2.1WithCancel

WithCancel函数可以用于创建一个Context对象,并返回一个可取消的上下文和一个取消函数。当调用取消函数时,会通知所有的Context对象和其子Context对象,使它们都取消执行。

funcWithCancel(parentContext)(ctxContext,cancelCancelFunc)

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"time"

funcmain(){

parent:=context.Background()

ctx,cancel:=context.WithCancel(parent)

gofunc(){

select{

case-ctx.Done():

fmt.Println(ctx.Err())

return

case-time.After(5*time.Second):

fmt.Println("workdone")

}

}()

time.Sleep(10*time.Second)

cancel()

time.Sleep(1*time.Second)

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithCancel函数创建一个子Context对象ctx,并返回一个可取消的上下文和一个取消函数cancel。接下来,我们在一个goroutine中使用select语句监听Context对象的Done方法和time.After函数的返回值,如果Done方法返回一个非nil的error,则说明Context已经被取消,否则说明time.After函数已经超时。在主函数中,我们调用cancel函数来通知Context对象和其子Context对象,使它们都取消执行。最后,我们使用time.Sleep函数让程序等待一段时间,以便观察Context的执行情况。

1.2.2WithDeadline

WithDeadline函数可以用于创建一个Context对象,并返回一个截止时间和一个取消函数。当超过截止时间时,会自动通知所有的Context对象和其子Context对象,使它们都取消执行。

funcWithDeadline(parentContext,deadlinetime.Time)(ctxContext,cancelCancelFunc)

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"time"

funcmain(){

parent:=context.Background()

ctx,cancel:=context.WithDeadline(parent,time.Now().Add(5*time.Second))

gofunc(){

select{

case-ctx.Done():

fmt.Println(ctx.Err())

return

case-time.After(10*time.Second):

fmt.Println("workdone")

}

}()

time.Sleep(20*time.Second)

cancel()

time.Sleep(1*time.Second)

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithDeadline函数创建一个子Context对象ctx,并返回一个截止时间和一个取消函数cancel。接下来,我们在一个goroutine中使用select语句监听Context对象的Done方法和time.After函数的返回值,如果Done方法返回一个非nil的error,则说明Context已经被取消,否则说明time.After函数已经超时。在主函数中,我们调用cancel函数来通知Context对象和其子Context对象,使它们都取消执行。最后,我们使用time.Sleep函数让程序等待一段时间,以便观察Context的执行情况。

1.2.3WithTimeout

WithTimeout函数可以用于创建一个Context对象,并返回一个超时时间和一个取消函数。当超过超时时间时,会自动通知所有的Context对象和其子Context对象,使它们都取消执行。

funcWithTimeout(parentContext,timeouttime.Duration)(ctxContext,cancelCancelFunc)

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"time"

funcmain(){

parent:=context.Background()

ctx,cancel:=context.WithTimeout(parent,5*time.Second)

gofunc(){

select{

case-ctx.Done():

fmt.Println(ctx.Err())

return

case-time.After(10*time.Second):

fmt.Println("workdone")

}

}()

time.Sleep(20*time.Second)

cancel()

time.Sleep(1*time.Second)

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithTimeout函数创建一个子Context对象ctx,并返回一个超时时间和一个取消函数cancel。接下来,我们在一个goroutine中使用select语句监听Context对象的Done方法和time.After函数的返回值,如果Done方法返回一个非nil的error,则说明Context已经被取消,否则说明time.After函数已经超时。在主函数中,我们调用cancel函数来通知Context对象和其子Context对象,使它们都取消执行。最后,我们使用time.Sleep函数让程序等待一段时间,以便观察Context的执行情况。

1.2.4WithValue

WithValue函数可以用于创建一个Context对象,并返回一个包含指定值的Context对象。这个值可以是任意类型的数据,可以是基本类型、结构体或者指针等。需要注意的是,这个值只在当前Context对象及其子Context对象中有效,对于其他Context对象来说是不可见的。

funcWithValue(parentContext,keyinterface{},valinterface{})Context

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

typeuserKeystruct{}

funcmain(){

parent:=context.Background()

ctx:=context.WithValue(parent,userKey{},"admin")

gofunc(){

ifuser,ok:=ctx.Value(userKey{}).(string);ok{

fmt.Printf("useris%s\n",user)

}else{

fmt.Println("userisnotfound")

}

}()

select{}

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithValue函数创建一个子Context对象ctx,并返回一个包含指定值的Context对象。接下来,我们在一个goroutine中使用ctx.Value函数获取Context对象中的值,并判断其类型是否为字符串类型。如果是,则输出其值,否则输出userisnotfound。在主函数中,我们使用select语句使程序一直运行,以便观察Context的执行情况。

2.Context的使用场景

2.1并发控制

一个很典型的使用场景是,当我们需要同时启动多个goroutine进行任务处理时,我们可以使用Context来控制这些goroutine的执行。在每个goroutine中,我们都可以检测Context对象是否被取消,如果是,则退出goroutine的执行,否则继续执行。

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"sync"

funcworker(ctxcontext.Context,wg*sync.WaitGroup){

deferwg.Done()

for{

select{

default:

fmt.Println("work")

case-ctx.Done():

return

}

}

funcmain(){

parent:=context.Background()

ctx,cancel:=context.WithCancel(parent)

varwgsync.WaitGroup

fori:=0;ii++{

wg.Add(1)

goworker(ctx,wg)

}

cancel()

wg.Wait()

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithCancel函数创建一个子Context对象ctx,并返回一个取消函数cancel。接下来,我们使用sync.WaitGroup来等待所有的goroutine执行完成。在主函数中,我们启动了三个goroutine来执行任务,同时使用cancel函数来通知这些goroutine取消执行。最后,我们使用Wait方法等待所有的goroutine执行完成。

2.2超时控制

另一个典型的使用场景是,当我们需要对一个操作设置一个超时时间时,我们可以使用Context来控制这个操作的执行时间。在操作执行超时时,我们可以通知Context对象和其子Context对象取消执行。

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"time"

funcwork(ctxcontext.Context){

for{

select{

default:

fmt.Println("work")

case-ctx.Done():

fmt.Println("workdone")

return

}

}

funcmain(){

parent:=context.Background()

ctx,cancel:=context.WithTimeout(parent,time.Second*5)

defercancel()

work(ctx)

}

在上面的代码中,我们首先使用context.Background()函数创建一个根Context对象parent,然后使用WithTimeout函数创建一个子Context对象ctx,并返回一个取消函数cancel。在work函数中,我们启动一个无限循环,不断输出work。同时,我们使用select语句来等待Context对象被取消。在主函数中,我们使用defer语句调用cancel函数,以确保Context对象被取消。由于我们在WithTimeout函数中设置了一个5秒的超时时间,因此当程序运行超过5秒时,work函数就会停止执行。

2.3数据库连接

在使用数据库连接时,我们通常需要保证连接池中的连接数量不会超过一定的阈值。如果连接池中的连接数量超过了阈值,则需要等待连接释放后再进行操作。在这种情况下,我们可以使用Context来控制连接的生命周期。

下面是一个示例代码:

packagemain

import(

"context"

"database/sql"

"fmt"

"sync"

"time"

_"/go-sql-driver/mysql"

constmaxConn=5

funcmain(){

db,err:=sql.Open("mysql","root:root@tcp(:3306)/test")

iferr!=nil{

panic(err)

}

deferdb.Close()

ctx,cancel:=context.WithTimeout(context.Background(),time.Second*5)

defercancel()

connCh:=make(chan*sql.Conn,maxConn)

varwgsync.WaitGroup

fori:=0;imaxConn;i++{

wg.Add(1)

gofunc(){

deferwg.Done()

for{

select{

case-ctx.Done():

return

default:

iflen(connCh)maxConn{

conn,err:=db.Conn(ctx)

iferr!=nil{

fmt.Println(err)

return

}

connCh-conn

}

}

}

}()

}

wg.Wait()

}

在上面的代码中,我们首先使用sql.Open函数打开一个MySQL数据库的连接,并返回一个DB对象db。接下来,我们使用WithTimeout函数创建一个Context对象ctx,并设置一个超时时间为5秒。同时,我们创建一个容量为maxConn的channel对象connCh,用于存储数据库连接。在goroutine中,我们使用select语句等待Context对象被取消。在每次循环中,我们检查连接池中连接的数量是否超过了阈值,如果没有,则使用db.Conn函数从连接池中获取一个新的连接,并将其存储到connCh中。最后,我们使用sync.WaitGroup等待所有的goroutine执行完成。

2.4HTTP请求

在使用HTTP请求时,我们通常需要设置一个超时时间,以确保请求能够在规定的时间内得到响应。在这种情况下,我们可以使用Context来控制HTTP请求的执行时间。

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"io/ioutil"

"net/http"

"time"

funcmain(){

client:=http.DefaultClient

ctx,cancel:=context.WithTimeout(context.Background(),time.Second*5)

defercancel()

req,err:=http.NewRequestWithContext(ctx,http.MethodGet,"",nil)

iferr!=nil{

fmt.Println(err)

return

}

resp,err:=client.Do(req)

iferr!=nil{

fmt.Println(err)

return

}

deferresp.Body.Close()

body,err:=ioutil.ReadAll(resp.Body)

iferr!=nil{

fmt.Println(err)

return

}

fmt.Println(string(body))

}

在上面的代码中,我们首先使用http.DefaultClient创建一个HTTP客户端对象client。接下来,我们使用WithTimeout函数创建一个Context对象ctx,并设置一个超时时间为5秒。同时,我们使用http.NewRequestWithContext函数创建一个HTTP请求对象req,并将Context对象ctx作为参数传递给该函数。在Do函数中,我们会自动将Context对象ctx传递给HTTP请求,并在超时时间到达后自动取消该请求。

2.5gRPC请求

在使用gRPC请求时,我们通常需要设置一个超时时间,以确保请求能够在规定的时间内得到响应。在这种情况下,我们可以使用Context来控制gRPC请求的执行时间。

下面是一个示例代码:

packagemain

import(

"context"

"fmt"

"log"

"time"

pb"/example/helloworld"

"/grpc"

const(

address

="localhost:50051"

defaultName="world"

funcmain(){

conn,err:=grpc.Dial(address,grpc.WithInsecure())

iferr!=nil{

log.Fatalf("didnotconnect:%v",err)

deferconn.Close()

c:=pb.NewGreeterClient(conn)

ctx,cancel:=context.WithTimeout(context.Background(),time.Second*5)

defercancel()

r,err:=c.SayHello(ctx,pb.HelloRequest{Name:defaultName}

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论