当前位置:首页 > 数码 > Context深化解读-Go言语初级个性 (contextual)

Context深化解读-Go言语初级个性 (contextual)

admin8个月前 (04-17)数码44

概述

在Go言语中,context(高低文)是一个十分关键的概念。

本文将讨论Go言语中context的用法,从基础概念到实践运行,将片面了解高低文的经常使用方法。

关键内容包括

什么是Context(高低文)

Context的基本用法:创立与传递

Context的敞开与超时

Context的值传递

实践运行场景:HTTP恳求的Context经常使用

数据库操作中的Context运行

自定义Context的成功

Context的生命周期治理

Context的留意事项

1.什么是Context(高低文)

在Go言语中,context(高低文)是一个接口,定义了四个方法:Deadline()、Done()、Err()和Value(keyinterface{})。

2.Context的基本用法:创立与传递

2.1创立Context

packagemnimport("context""fmt")funcmain(){//创立一个空的Contextctx:=context.Background()//创立一个带有敞开信号的Context_,cancel:=context.WithCancel(ctx)defercancel()//提前调用cancel,确保在函数分开时敞开Contextfmt.Println("Context创立成功")}

以上代码展示了如何创立一个空的context和一个带有敞开信号的context。

经常使用context.WithCancel(parent)可以创立一个带有敞开信号的子context。

在调用cancel函数时,一切基于该context的操作都会收到敞开信号。

2.2传递Context

packagemainimport("context""fmt""time")funcworker(ctxcontext.Context){for{select{case<-ctx.Done():fmt.Println("收到敞开信号,义务完结")returndefault:fmt.Println("正在口头义务...")time.Sleep(1*time.Second)}}}funcmain(){ctx:=context.Background()ctxWithCancel,cancel:=context.WithCancel(ctx)defercancel()goworker(ctxWithCancel)time.Sleep(3*time.Second)cancel()//发送敞开信号time.Sleep(1*time.Second)}

在下面例子中,创立了一个带有敞开信号的context,并将它传递给一个goroutine中的义务。

调用cancel函数,可以发送敞开信号,终止义务的口头。

3.Context的敞开与超时

contextual

3.1经常使用Context成功敞开

packagemainimport("context""fmt""time")funcmain(){ctx:=context.Background()ctxWithCancel,cancel:=context.WithCancel(ctx)gofunc(){select{case<-ctxWithCancel.Done():fmt.Println("收到敞开信号,义务完结")case<-time.After(2*time.Second):fmt.Println("义务成功")}}()time.Sleep(1*time.Second)cancel()//发送敞开信号time.Sleep(1*time.Second)}

在这个例子中,经常使用time.After函数模拟一个长期间运转的义务。

假设义务在2秒内成功,就打印义务成功,否则在接纳到敞开信号后打印收到敞开信号,义务完结。

3.2经常使用Context成功超时控制

packagemainimport("context""fmt""time")funcmain(){ctx:=context.Background()ctxWithTimeout,cancel:=context.WithTimeout(ctx,2*time.Second)defercancel()select{case<-ctxWithTimeout.Done():fmt.Println("超时,义务完结")case<-time.After(1*time.Second):fmt.Println("义务成功")}}

在下面例子中,经常使用context.WithTimeout(parent,duration)函数创立了一个在2秒后智能敞开的context

假设义务在1秒内成功,就打印义务成功,否则在2秒后打印超时,义务完结。

4.Context的值传递

4.1经常使用WithValue传递值

packagemainimport("context""fmt")typekeystringfuncmain(){ctx:=context.WithValue(context.Background(),key("name"),"Alice")value:=ctx.Value(key("name"))fmt.Println("Name:",value)}

在下面例子中,经常使用context.WithValue(parent,key,value)函数在context中传递了一个键值对。

经常使用ctx.Value(key)函数可以失掉传递的值。

5.实践运行场景:HTTP恳求的Context经常使用

5.1经常使用Context处置HTTP恳求

packagemainimport("fmt""/http""time")funchandler(whttp.ResponseWriter,r*http.Request){ctx:=r.Context()select{case<-time.After(3*time.Second):fmt.Fprintln(w,"Hello,World!")case<-ctx.Done():err:=ctx.Err()fmt.Println("处置恳求:",err)http.Error(w,err.Error(),http.StatusInternalServerError)}}funcmain(){http.HandleFunc("/",handler)http.ListenAndServe(":8080",nil)}

在下面例子中,经常使用http.Request结构体中的Context()方法失掉到恳求的context,并在处置恳求时设置了3秒的超时期间。

假设恳求在3秒内成功,就前往Hello,World!,否则前往一个失误。

5.2处置HTTP恳求的超时与敞开

packagemainimport("context""fmt""net/http""time")funchandler(whttp.ResponseWriter,r*http.Request){ctx,cancel:=context.WithTimeout(r.Context(),2*time.Second)defercancel()select{case<-time.After(3*time.Second):fmt.Fprintln(w,"Hello,World!")case<-ctx.Done():err:=ctx.Err()fmt.Println("处置恳求:",err)http.Error(w,err.Error(),http.StatusInternalServerError)}}funcmain(){http.HandleFunc("/",handler)http.ListenAndServe(":8080",nil)}

在下面例子中,经常使用context.WithTimeout(parent,duration)函数为每个恳求设置了2秒的超时期间。

假设恳求在2秒内成功,就前往Hello,World!,否则前往一个失误。

6.数据库操作中的Context运行

6.1经常使用Context控制数据库查问的超时

packagemainimport("context""database/sql""fmt""time"_".com/go-sql-driver/")funcmain(){db,err:=sql.Open("mysql","username:pass@tcp(localhost:3306)/database")iferr!=nil{fmt.Println(err)return}deferdb.Close()ctx,cancel:=context.WithTimeout(context.Background(),2*time.Second)defercancel()rows,err:=db.QueryContext(ctx,"SELECT*FROMusers")iferr!=nil{fmt.Println("查问出错:",err)return}deferrows.Close()//处置查问结果}

在下面例子中,经常使用context.WithTimeout(parent,duration)函数为数据库查问设置了2秒的超时期间,确保在2秒内成功查问操作。

假设查问超时,程序会收到context的敞开信号,从而终止数据库查问。

6.2经常使用Context敞开长期间运转的数据库事务

packagemainimport("context""database/sql""fmt""time"_"github.com/go-sql-driver/mysql")funcmain(){db,err:=sql.Open("mysql","username:password@tcp(localhost:3306)/database")iferr!=nil{fmt.Println(err)return}deferdb.Close()tx,err:=db.Begin()iferr!=nil{fmt.Println(err)return}ctx,cancel:=context.WithTimeout(context.Background(),2*time.Second)defercancel()gofunc(){<-ctx.Done()fmt.Println("接纳到敞开信号,回滚事务")tx.Rollback()}()//口头长期间运转的事务操作//...err=tx.Commit()iferr!=nil{fmt.Println("提交事务出错:",err)return}fmt.Println("事务提交成功")}

在这个例子中,经常使用context.WithTimeout(parent,duration)函数为数据库事务设置了2秒的超时期间。

同时经常使用goroutine监听context的敞开信号。

在2秒内事务没有成功,程序会收到context的敞开信号,从而回滚事务。

7.自定义Context的成功

7.1成功自定义的Context类型

packagemainimport("context""fmt""time")typeMyContextstruct{context.ContextRequestIDstring}funcWithRequestID(ctxcontext.Context,requestIDstring)*MyContext{return&MyContext{Context:ctx,RequestID:requestID,}}func(c*MyContext)Deadline()(deadlinetime.Time,okbool){returnc.Context.Deadline()}func(c*MyContext)Done()<-chanstruct{}{returnc.Context.Done()}func(c*MyContext)Err()error{returnc.Context.Err()}func(c*MyContext)Value(keyinterface{})interface{}{ifkey=="requestID"{returnc.RequestID}returnc.Context.Value(key)}funcmain(){ctx:=context.Background()ctxWithRequestID:=WithRequestID(ctx,"12345")requestID:=ctxWithRequestID.Value("requestID").(string)fmt.Println("RequestID:",requestID)}

在这个例子中,成功了一个自定义的MyContext类型,它蕴含了一个RequestID字段。

经过WithRequestID函数,可以在原有的context上附加一个RequestID值,而后在须要的时刻失掉这个值。

7.2自定义Context的运行场景

自定义context类型的运行场景十分宽泛,比如在微服务架构中,

8.Context的生命周期治理

8.1Context的生命周期

context.Context是无法变的,一旦创立就不能修正。它的值在传递时是安保的,可以被多个goroutine共享。

在一个恳求处置的生命周期内,通常会创立一个顶级的Context,而后从这个顶级的Context派生出子Context,传递给详细的处置函数。

8.2Context的敞开

当恳求处置成功或许出现失误时,应该被动调用context.WithCancel或context.WithTimeout等函数创立的Context的Cancel函数来通知子goroutines中止上班。

这样可以确保资源被及时监禁,防止goroutine走漏。

funchandleRequest(ctxcontext.Context){//派生一个新的Context,设置超时期间ctx,cancel:=context.WithTimeout(ctx,time.Second)defercancel()//确保在函数分开时调用cancel,防止资源走漏//在这个新的Context下启动操作//...}

8.3Context的传递

在函数之间传递Context时,确保传递的是必要的最小消息。防止在函数间传递整个Context,而是选用传递Context的衍动物。

如context.WithValue创立的Context,其中蕴含了特定的键值对消息。

funcmiddleware(nexthttp.Handler)http.Handler{returnhttp.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){//从恳求中失掉特定的消息userID:=extractUserID(r)//将特定的消息存储到Context中ctx:=context.WithValue(r.Context(),userIDKey,userID)//将新的Context传递给下一个处置器next.ServeHTTP(w,r.WithContext(ctx))})}

9.Context的留意事项

9.1不要在函数签名中传递Context

防止在函数的参数列表中传递context.Context,除非确实须要用到它。

假设函数的逻辑只有要经常使用Context的局部性能,那么最好只传递它须要的详细消息,而不是整个Context。

//不介绍的做法funcprocessRequest(ctxcontext.Context){//...}//介绍的做法funcprocessRequest(userIDint,timeouttime.Duration){//经常使用userID和timeout,而不是整个Context}

9.2防止在结构体中滥用Context

不要在结构体中保留context.Context,由于它会参与结构体的复杂性。

若是须要在结构体中经常使用Context的值,最好将须要的值作为结构体的字段传递出去。

typeMyStructstruct{//不介绍的做法Ctxcontext.Context//介绍的做法UserIDint}

golang自动更新怎么实现

首先理解是错的,不管用户态的API(syscall)是否是同步还是异步,在kernel层面都是异步的。 其实实现原理很简单,就是利用C(嵌入汇编)语言可以直接修改寄存器(setcontext/setjmp/longjmp均是类似原理,修改程序指针eip实现跳转,栈指针实现上线文切换)来实现从func_a调进去,从func_b返回出来这种行为。 对于golang来说,func_a/func_b属于不同的goroutine,从而就实现了goroutine的调度切换。 另外对于所有可能阻塞的syscall,golang对其进行了封装,底层实际是epoll方式做的,注册回调后切换到另一个runnable的goroutine。

(知乎) golang的goroutine是如何实现的

Go runtime的调度器:在了解Go的运行时的scheduler之前,需要先了解为什么需要它,因为我们可能会想,OS内核不是已经有一个线程scheduler了嘛?熟悉POSIX API的人都知道,POSIX的方案在很大程度上是对Unix process进场模型的一个逻辑描述和扩展,两者有很多相似的地方。 Thread有自己的信号掩码,CPU affinity等。 但是很多特征对于Go程序来说都是累赘。 尤其是context上下文切换的耗时。 另一个原因是Go的废品回收需要所有的goroutine停止,使得内存在一个一致的状态。 废品回收的时间点是不确定的,如果依靠OS自身的scheduler来调度,那么会有大量的线程需要停止工作。 单独的开发一个GO得调度器,可以是其知道在什么时候内存状态是一致的,也就是说,当开始废品回收时,运行时只需要为当时正在CPU核上运行的那个线程等待即可,而不是等待所有的线程。 用户空间线程和内核空间线程之间的映射关系有:N:1,1:1和M:NN:1是说,多个(N)用户线程始终在一个内核线程上跑,context上下文切换确实很快,但是无法真正的利用多核。 1:1是说,一个用户线程就只在一个内核线程上跑,这时可以利用多核,但是上下文switch很慢。 M:N是说, 多个goroutine在多个内核线程上跑,这个看似可以集齐上面两者的优势,但是无疑增加了调度的难度。

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: Go言语

“Context深化解读-Go言语初级个性 (contextual)” 的相关文章

Go言语成功GoF设计形式-适配器形式 (成功的言语交际)

Go言语成功GoF设计形式-适配器形式 (成功的言语交际)

简介 适配器形式 (Adapter)是最罕用的结构型形式之一,在事实生存中,适配器形式也是处处可见,比如电源插头转换器,它可以让英式的插头上班在中式的插座上。 GoF对它的...

初学者的全面入门指南-20小时极速把握Go言语 (初学者建议)

初学者的全面入门指南-20小时极速把握Go言语 (初学者建议)

Go言语是由开发的一种高效、繁复和并发性强的编程言语,其设计指标是使得程序员能够愈加容易地创立牢靠、高效的软件。虽然Go言语的语法相对其余编程言语来说或许愈加生疏,然而学习Go言语的门槛却很低。以...

把握人工智能与-Go-言语的无缝整合指南 (把握人工智能等新科技革命浪潮的必然要求)

把握人工智能与-Go-言语的无缝整合指南 (把握人工智能等新科技革命浪潮的必然要求)

在当今运行开发畛域,相似target=_blankclass=infotextkey>OpenAIAPI等生成式AI技术的蓬勃开展正在彻底扭转着运行开发的格式。/target=_blankc...

十个令人惊叹的Go言语技巧-让你的代码愈加优雅 (十个令人惊叹的成语)

十个令人惊叹的Go言语技巧-让你的代码愈加优雅 (十个令人惊叹的成语)

在开发消费名目的环节中,我留意到经常会发现自己在重复编写代码,经常使用某些技巧时没无看法到,直到起初回忆上班时才看法到。 为了处置这个疑问,我开发了一种处置打算,对我来说十分有协助,我感觉对...

Go言语Context运行全攻略-异步编程利器 (go言语判断常量)

Go言语Context运行全攻略-异步编程利器 (go言语判断常量)

概述 在Go言语中,Context(高低文)是一个十分关键的概念,特意是在处置恳求时。 准许在恳求的整个生命周期内传递数据、控制恳求的敞开、处置超时等。 本文将引见Go言语中Co...