民权网站建设食品网站模板下载
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:22
当前位置: 首页 > news >正文
民权网站建设,食品网站模板下载,怎样做网站seo,软文平台发布GO的并发模式Context 文章目录GO的并发模式Context一、介绍二、Context三、context的衍生四、示例#xff1a;Google Web Search4.1 server程序4.2 userip 包4.3 google 包五、使用context包中程序实体实现sync.WaitGroup同样的功能#xff08;1#xff09;使用sync.WaitGro…GO的并发模式Context 文章目录GO的并发模式Context一、介绍二、Context三、context的衍生四、示例Google Web Search4.1 server程序4.2 userip 包4.3 google 包五、使用context包中程序实体实现sync.WaitGroup同样的功能1使用sync.WaitGroup实现一对多goroutine协作流程多同步工具2使用context包中程序实体来实现六、Context的特点1Context介绍上下文、树、根节点2四个延伸Context值的函数3理解context包中“可撤销”和“撤销一个context”4撤销信号在上下文树中的传播5通过Context携带数据一、介绍
参考Go的并发模式Context。
在Go服务中每一个传入进来的请求都将在它自己的goroutine中被处理。请求处理器通常启动额外的goroutines去访问后端例如数据库和RPC服务。基于请求进行工作的goroutines通常需要获取请求的特殊值例如最终的用户身份、授权令牌、和请求的最后期限。当一个请求被取消或者超时所有给予请求进行工作的goroutines应该立刻退出以便于系统能够回收任何它们使用的资源。
在Google中我们开发了一个context包以便于更容易的传递请求范围的值、取消信号、和最终的读取API边界的期限涉及处理请求中的所有goroutines。这个包是公开可用的被称为context。这篇文章将详细描述如何使用这个包并提供完成的工作示例。
二、Context
context包的核心代码是Context类型
// 一个Context懈怠了终止日期、取消信号、和请求范围的值读取API的边界。
// 它的方法对于多个goroutines同时使用是安全的。
type Context interface{// Done方法返回一个通道。当Context被取消或者超时的时候这个通道将被关闭。Done() -chan struct{}// Err 用于表明为什么Context是被取消。通常是由于Done的通道被关闭Err() error// Deadline 当无论什么原因这个context被取消的时候返回一个时间Deadline() (deadline time.Time, ok bool)// Value 返回与key对应的值如果没有就返回nilValue(key interface{}) interface{}
}Done方法返回一个通道这个通道给那些代表Context运行函数的取消信号当通道被关闭当通道关闭这些函数应该立刻放弃它们的工作并返回。Err函数返回一个错误表明为什么Context 是被取消。在管道和消除这篇文件中更详细的探讨了Done 通道的完整用法。
一个Context没有一个Cancel方法和Done通道仅用于接收的原因相同函数通常是接收到一个取消信号而不是发送一个信号。尤其是当一个父操作为子操作启动goroutines这些子操作不应该能够去取消父操作。相反WithCancel函数在下面描述提供了一个方法用于取消一个新的Context值。
一个Context被多个goroutine同时使用时安全的。代码能够传统同一个Context给任何数量的goroutines并取消Context以向所有goroutines发出信号。
Deadline方法允许函数去确定是否它们应该都开始工作如果剩余的时间太少它可能是没有价值的。代码也可以使用最后期限给I/O操作设置超时时间。
Value允许一个Context可以携带请求范围的值。这个数据对多个goroutines同时使用必须是安全的。
三、context的衍生
context包提供了一个函数用于从已经存在的Contexts里衍生新的Context值。这些值形成了树当一个Context被取消所有从它衍生出来的Context也将被取消。
Background是任何Context树的根它永远不会被取消
/// Background 返回一个空的context。它绝不会被取消没有最后期限不带值。
// Background 通常在main、init、test中使用作为请求顶层的Context。
func Background() ContextWithCancel和WithTimeout返回衍生的Context值。它能比它父Context更早的取消。
关联一个传入请求的Context当请求处理返回的时候它通常能够被取消。
当使用多个副本的时候WithCancel对于·取消冗余的请求也很有用。
WithTimeout对于设置后台服务请求的截止日期很有用。
// WithCancel 返回一个父Context的拷贝它的Done通道在父级关闭时立即关闭。
// Done 完成关闭或取消
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// 一个CancelFunc 取消一个Context
type CancelFunc func()// WithTimeout 返回一个父级的副本它的Done 通道在父级关闭时立即关闭。
// 关闭 Done、调用取消或超时。
// 新Context的最后期限是现在超时时间 和 父的截止日期如果有的话中较早的一个。
// 如果计时器仍在运行取消函数将释放其资源。
func WithTimeout(parent Context, timeout time.Duration) (context, CancelFunc)WithContext 提供了一个方法去将请求范围的值和Context进行关联。
// WithValue 返回一个父Context的副本 它的Value方法返回key对应的val。
func WithValue(parent Context, key interface{}, value interface{}) Context 最好查看如何使用context包的方法是通过工作示例。
四、示例Google Web Search
我们的示例是一个HTTP服务它处理像/search?qgolangtimeout1s这样的URL通过转发golang 的查询到Google Web Search API并展示结果。这个timeout参数告诉服务端在一段时间后取消请求。
代码被分到三个包中
server 提供main函数并处理/search请求userip 提供用于从请求中提取用户ip地址的函数并将它和Context进行关联google 提供查询函数用于发送请求到 Google
4.1 server程序
server程序处理像/search?qgolang的请求通过提供前几个对golang在Google上的查询结果。它注册handleSearch来处理/search端点。这个处理创建了一个初始的Context称之为ctx当处理返回的时候安排它取消。如果请求包含timeoutURL参数当超时时间通过Context将自动被取消
func main() {// 注册 handleSearch 来处理 /search 端点http.HandleFunc(/search, handleSearch)
}func handleSearch(w http.ResponseWriter, req *http.Request) {// ctx 是 这个处理器的 Context。// 调用 cancel 关闭ctx.Done 的通道。这是对这个请求的取消信号被处理器启动。var (ctx context.Contextcancel context.CancelFunc)// 获取超时时间timeout, err : time.ParseDuration(req.FormValue(timeout))if err nil {// 请求有超时时间因此创建一个context当超时时间到期后它将自动取消ctx, cancel context.WithTimeout(context.Background(), timeout)} else {ctx, cancel context.WithCancel(context.Background())}// handleSearch返回之后立刻取消ctxdefer cancel()处理器从请求中提取查询并通过调用userip包提取客户端的IP地址。客户端的IP地址对后端是需要的因此handleSearch将它绑定到ctx上 // 检查查询请求query : req.FormValue(q)if query {http.Error(w, no query, http.StatusBadRequest)return}userIP, err : userip.FromRequest(req)if err ! nil {http.Error(w, err.Error(), http.StatusBadRequest)return}ctx userip.NewContext(ctx, userIP)handle调用google.Search方法带有ctx和query // 运行Google 搜索并打印结果start : time.Now()results, err : google.Search(ctx, query)elapsed : time.Since(start)如果查询成功处理器渲染结果 if err ! nil {http.Error(w, err.Error(), http.StatusInternalServerError)return}if err : resultsTemplate.Execute(w, struct {Results google.ResultsTimeout, Elapsed time.Duration}{Results: results,Timeout: timeout,Elapsed: elapsed,}); err ! nil {log.Print(err)return}4.2 userip 包
userip包提供了用于从请求中提取用户ip地址的函数并将它关联到Context中。一个Context提供了键-值对的映射在这里键和值都是interface{}类型。键的类型必须支持判等操作值在被多个goroutine同时使用多时候必须是安全的。像userip包隐藏了这个映射的细节并提供了抢类型用于读取一个特定的Context值。
为了避免键的冲突userip定义了一个不对外开放的类型key并使用这个类型的值作为context的键。
// 密钥类型未导出以防止与其他包中定义的上下文密钥发生冲突。
type key int// userIPKey 是一个context key用于用户的IP地址。它的零值是随意定的。
// 如果这个包定义了其它的context key。它们应该是不同的数值。
const userIPKey key 0FromRequest从一个http.Request中提取一个userIP
func FromRequest(req *http.Request) (net.IP, error) {ip, _, err : net.SplitHostPort(req.RemoteAddr)if err ! nil {return nil, fmt.Errorf(userip: %q is not IP:port, req.RemoteAddr)}NewContext返回一个新的Context携带了一个提供的userIP值。
// 如果ip地址存在 FromContext 从ctx中提取用户的ip地址
func FromContext(ctx context.Context) (net.IP, bool) {// 如果 ctx 没有针对key的值ctx.Value 将返回 nil// net.IP 类型的断言 将对 nil 返回 okfalseuserIP, ok : ctx.Value(userIPKey).(net.IP)return userIP, ok
}4.3 google 包
google.Search函数创建一个HTTP请求给Google Web Search API并解析JSON编码的结果。它接受一个Context参数在请求被处理期间如果ctx.Done被关闭它将立即返回。
Google Web Search API请求包含了查询请求并使用IP作为请求参数。
// Search 发送查询到 Google 搜索并返回结果
func Search(ctx context.Context, query string) (Results, error) {// 准备Google 搜索 API 请求req, err : http.NewRequest(GET, https://ajax.googleapis.com/ajax/services/search/web?v1.0, nil)if err ! nil {return nil, err}q : req.URL.Query()q.Set(q, query)// 如果 ctx 懈怠了用户的IP地址将它转发给服务器。// Google APIs 使用用户的IP地址来区分服务器发起的请求和最终用户的请求if userIP, ok : userip.FromContext(ctx); ok {q.Set(userip, userIP.String())}req.URL.RawQuery q.Encode()Search使用一个有用的函数httpDo用于发出http请求并在请求或响应在处理期间如果ctx.Done 被关闭那么就取消它们。Search将一个闭包传递给httpDo用于处理http响应。 // 发出HTTP请求并处理响应。// 如果 ctx.Done 是被取消 httpDo 函数将取消请求。var results Resultserr httpDo(ctx, req, func(resp *http.Response, err error) error {if err ! nil {return err}defer resp.Body.Close()// 解析JSON格式的查询结果// https://developers.google.com/web-search/docs/#fonjevar data struct {ResponseData struct {Results []struct {TitleNoFormatting stringURL string}}}if err : json.NewDecoder(resp.Body).Decode(data); err ! nil {return err}for _, res : range data.ResponseData.Results {results append(results, Result{Title: res.TitleNoFormatting, URL: res.URL})}return nil})// httpDo 等待我们提供的闭包返回所以在这里读取结果是安全的。return results, errhttpDo 函数返回http请求并在一个新的goroutine中处理它的响应。在goroutine离开之前如果ctx.Done被关闭它将取消请求。
// httpDo 发起 HTTP 请求 并使用响应调用 f 。
// 如果 ctx.Done 在请求或f函数运行期间被关闭httpDo取消那个请求等待f离开并返回ctx.Err。
// 否则 返回 f 的 error
func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {// 在一个goroutine中 运行HTTP请求 并传递响应给 fc : make(chan error, 1)req req.WithContext(ctx)go func() { c - f(http.DefaultClient.Do(req)) }()select {case -ctx.Done():-c // 为了等待f 函数返回return ctx.Err()case err : -c:return err}
}五、使用context包中程序实体实现sync.WaitGroup同样的功能
1使用sync.WaitGroup实现一对多goroutine协作流程多同步工具
package mainimport (fmtsyncsync/atomic
)func main() {coordinateWithWaitGroup()
}func coordinateWithWaitGroup() {total : 12stride : 3var num int32fmt.Printf(The number: %d [with sync.WaitGroup]\n, num)var wg sync.WaitGroupfor i : 1; i total; i stride {wg.Add(stride)for j : 0; j stride; j {go addNum(num, ij, wg.Done)}wg.Wait()}
}func addNum(numP *int32, id int, deferFunc func()) {defer func() {deferFunc()}()for i : 0; ; i {currNum : atomic.LoadInt32(numP)newNum : currNum 1if atomic.CompareAndSwapInt32(numP, currNum, newNum) {fmt.Printf(The number: %d [%d-%d]\n, newNum, id, i)break} else {fmt.Printf(The CAS option failed. [%d-%d]\n, id, i)}}
}
2使用context包中程序实体来实现
func coordinateWithContext() {total : 12var num int32fmt.Printf(The number: %d [with context.Context]\n, num)cxt, cancelFunc : context.WithCancel(context.Background())for i : 1; i total; i {go addNum(num, i, func() {// 如果所有的addNum函数都执行完毕那么就立即分发子任务的goroutine// 这里分发子任务的goroutine就是执行 coordinateWithContext 函数的goroutine.if atomic.LoadInt32(num) int32(total) {// -cxt.Done() 针对该函数返回的通道进行接收操作。// cancelFunc() 函数被调用针对该通道的接收会马上结束。// 所以这样做就可以实现“等待所有的addNum函数都执行完毕”的功能cancelFunc()}})}-cxt.Done()fmt.Println(end.)
}$ go run demo01.go
The number: 0 [with context.Context]
The number: 1 [12-0]
The number: 3 [6-0]
The number: 4 [7-0]
The number: 5 [8-0]
The number: 6 [9-0]
The number: 2 [5-0]
The number: 8 [10-0]
The number: 10 [11-0]
The number: 11 [1-0]
The number: 9 [3-0]
The number: 7 [2-0]
end.执行发现有时候结果并不对。
六、Context的特点
1Context介绍上下文、树、根节点
Context类型是一种非常通用的同步工具它的值不但可以任意扩散还可以被用来传递额外的信息和信号。 Context类型可以提供一类代表上下文的值。此类值是并发安全的也就是说它可以被传播给多个goroutine。 Context类型的值可以繁衍这意味着可以通过一个Context值产生任意个子值。这些子值可以携带其父值的属性和数据也可以响应我们通过父值传递的信号。
所有的Context值共同构成了一颗代表上下文全貌的树形结构。这棵树的树根或称上下文的根节点是一个已经在context包中预定义好的Context值它是全局唯一的。通过context.Background函数可以取到它。 上下文根节点仅仅是一个最基本的支点它不提供任何额外的功能。也就是说它既不可以被撤销也不能携带任何数据。 2四个延伸Context值的函数
在context包中包含四个用于繁衍Context值的函数即WithCancel 、WithDeadline、WithTimeout、WithValue。 四个函数的第一个参数的类型都是context.Context名称都为parent。 顾名思义这个位置上的参数对应的都是它们将会产生的Context 值的父值。 WithCancel用于产生一个可撤销的parent的子值 通过调用该函数可以得到一个衍生自上下文根节点的Context值和一个用于发送撤销信号的函数。 cxt, cancelFunc : context.WithCancel(context.Background())WithDeadline和 WithTimeout函数都是用来产生一个会定时撤销的parent的子值。 WithValue函数可以用来产生一个会携带额外数据的parent的子值。
3理解context包中“可撤销”和“撤销一个context”
在Context接口中有两个与撤销息息相关的方法 Done方法会返回一个元素类型为struct{}的接收通道。让使用方感知撤销信号 这个接收通道的用途并不是传递元素值而是让调用方法感知“撤销”当前Context值的那个信号。 一旦当前的Contex值被撤销这里的接收通道会立刻关闭。对于一个未包含任何元素值的通道来说它的关闭会使任何针对它的接收操作立即结束。 Err方法。让使用方得到撤销的具体原因。 Context的Err方法的结果是error类型并且其值值可能等于context.Canceled变量的值或者context.DeadlineExceeded 变量的值。 context.Canceled 表示手动撤销context.DeadlineExceeded 表示由于给定的过期时间已到而导致的撤销。
对“撤销“和”可撤销“对理解
如果把”撤销“当作名词理解指的是用来表达“撤销”状态的信号如果把“撤销”当作动词理解指的是对撤销信号的表达“可撤销”指的是具有传达这种撤销信号的能力
理解context.WithCancel 通过调用context.WithCancel可以产生一个可撤销的Context值还会获得一个用于触发撤销信号的函数。 通过调用这个用于触发撤销信号的函数 撤销信号会被传达给这个Context值并由它的Done方法的结果值一个接收通道表达出来 撤销函数只负责触发信号而对应的可撤销的Context值也只负责传达信号。它们都不会去管后面具体的“撤销”操作。 实际上我们的代码在感知到撤销操作以后可以任意的操作Context对此并没有任何的约束。
4撤销信号在上下文树中的传播
在contex包中包含四个用于繁衍Context值的函数其中WithCancel、WithDeadline、WithTimeout都是被用于基于给定的Context值产生可撤销的子值的。 context.WithValue函数得到的Context值可不撤销撤销信号在被传播时若遇到它们则会直接跨过并试图将信号直接传给它们的子值。 context 包的WithCancel函数在被调用后会产生两个结果值第一个结果值就是那个可撤销的Context值而第二个则是用于触发撤销信号的函数。
撤销函数被调用之后对应的Context值会先关闭它内部的接收通道也就是它的Done方法会返回的那个通道。然后它会向它的所有子值或者说子节点传达撤销信号。这些子值会如法炮制把撤销信号继续传播下去。最后这个Context值会断开它与其父值之间的关联。
通过调用context包中的WithDealline函数或者WithTimeout函数生成的Context值也是可撤销的。它们不但可以被手动撤销还会依据在生成时被给定的过期时间自动地进行撤销。 这里的定时撤销是借助内部的计时器来做。 当过期时间到达时WithDeadline和WithTimeout这两个Context值当行为与Context值被手动撤销时的行为几乎是一致的。只不过WithDeadline和WithTimeout会在最后停止并释放掉其内部的计时器。
5通过Context携带数据
WithValue函数在产生新的Context值的时候需要三个参数父值、键、值。 与字典对于键的约束类似这里键的类型必须是可判等的。原因很简单我们从中获取数据的时候它需要根据给定的键来查找对应的值。只不过这种Context值并不是用字典来存储键和值的只是简单的存储在响应的字段中。 Context类型的Value方法就是用来获取数据的。在我们调用含数据的Context值的Value方法时它会先判断给定的键是否与当前值中存储的键相等。如果相等就把该值中的存储的值直接返回否则就到父值中继续寻找。如果父值中仍然未存储相等的键那么该方法就会沿着上下文根节点的方向一路查找下去。
注意除了含数据的Context值以外其它几种Context值都无法携带数据。因此Context值的Value方法在沿路查找的时候会直接跨过那几种值。
Context接口并没有提供改变数据的方法。因此在通常情况下我们只能通过在上下文树中添加含数据的Context值来存储新的数据或者通过撤销此种值的父值丢弃掉相应的数据。如果你存储在这里的数据可以从外部改变那么必须自行保证安全。
- 上一篇: 民企厂房建设招标网站网站建设要学哪些软件有哪些
- 下一篇: 民宿网站的建设肥城网站开发公司
相关文章
-
民企厂房建设招标网站网站建设要学哪些软件有哪些
民企厂房建设招标网站网站建设要学哪些软件有哪些
- 技术栈
- 2026年03月21日
-
民间it网站建设常德天鹰建设有限公司网站
民间it网站建设常德天鹰建设有限公司网站
- 技术栈
- 2026年03月21日
-
民非企业网站建设费怎么记账深圳设计网站培训
民非企业网站建设费怎么记账深圳设计网站培训
- 技术栈
- 2026年03月21日
-
民宿网站的建设肥城网站开发公司
民宿网站的建设肥城网站开发公司
- 技术栈
- 2026年03月21日
-
民治营销型网站制作wordpress 4.5.3 主题
民治营销型网站制作wordpress 4.5.3 主题
- 技术栈
- 2026年03月21日
-
闵行交大附近网站建设兰陵住房建设局网站
闵行交大附近网站建设兰陵住房建设局网站
- 技术栈
- 2026年03月21日
