GOLANG的context和并发模型

GOLANG的context和并发模型,第1张

概述GOLANG1.7新增了context,最初这个package是在golang.org/x/net/context中的,后来证实对很多程序都是必须的,就纳入了标准库。 对context的介绍是在context,读这个blog之前,要先读pipelines(pipelines提出了使用close(chan)的方式广播退出事件)。 一般来说,context是用在request的处理,例如http请求的

GOLANG1.7新增了context,最初这个package是在golang.org/x/net/context中的,后来证实对很多程序都是必须的,就纳入了标准库。

对context的介绍是在context,读这个blog之前,要先读pipelines(pipelines提出了使用close(chan)的方式广播退出事件)。

一般来说,context是用在request的处理,例如http请求的处理中,可能会开启多个goroutine,比如:

http.HandleFunc(func(w http.ResponseWriter,r *http.Request){    wg := sync.WaitGroup{}    wg.Add(2)    var result0 string    go func(){        defer wg.Done()        res0,err := http.Get("http://server0/API0")        // Parse res0 to result0.    }()    var result1 string    go func(){        defer wg.Done()        res1,err := http.Get("http://server1/API1")        // Parse res1 to result1.    }()    wg.Wait()    w.Write([]byte(result0 + result1)})

实际上这个程序是不能这么写的,如果这两个goroutine请求的时间比较长,让用户一直等着么?如果用户取消了请求,关闭了连接呢?如果用户指定了超时时间呢?

另外,考虑如何关闭一个http服务器,比如需要关闭Listener后重新侦听一个新的端口:

var server *http.Server    go func() {        server = &http.Server{Addr: addr,Handler: nil}        if err = server.ListenAndServe(); err != nil {            ol.E(nil,"API serve Failed,err is",err)            return        }    }()

我们如何确保goroutine已经退出后,然后才返回重新开启服务器?如果只是server.Close(或者GO1.8之前用Listener.Close),如何接收外部的事件?如果是goroutine自己的问题,例如端口占用了,如何通知程序退出?直接用os.Exit明显是太粗鲁的做法。

在context中,有一段话非常关键:

A Context does not have a Cancel method for the same reason the Done channel is receive-only: the function receiving a cancelation signal is usually not the one that sends the signal. In particular,when a parent operation starts goroutines for sub-operations,those sub-operations should not be able to cancel the parent. Instead,the WithCancel function (described below) provIDes a way to cancel a new Context value.

也就是说,context没有提供Cancel方法,因为parent goroutine会调用Cancel,在所有sub goroutines中只需要读context.Done()就可以,也就是只是接收退出信号。

还有一处地方,说明了Context应该放在参数中:

Server frameworks that want to build on Context should provIDe implementations of Context to brIDge between their packages and those that expect a Context parameter. Their clIEnt librarIEs would then accept a Context from the calling code. By establishing a common interface for request-scoped data and cancelation,Context makes it easIEr for package developers to share code for creating scalable services.

只读取chan的好处是,可以使用close(chan)方式通知所有goroutine退出。

使用context和WaitGroup,同步和取消服务器的例子:

func httpAPIServe(ctx context.Context) {    http.HandleFunc("/API",func(w http.ResponseWriter,r *http.Request) {    })    ctx,cancel := context.WithCancel(ctx)    wg := &sync.WaitGroup{}    defer wg.Wait()    wg.Add(1)    var server *http.Server    go func(ctx context.Context) {        defer wg.Done()        defer cancel()        server = &http.Server{Addr: addr,Handler: nil}        _ = server.ListenAndServe()    }(ctx)    select {    case <-ctx.Done():        server.Close()    }}wg := sync.WaitGroup{}defer wg.Wait()ctx,cancel := context.WithCancel(context.Background())wg.Add(1)go func(ctx context.Context) {    defer wg.Done()    defer cancel()    httpAPIServe(ctx)}(ctx)wg.Add(1)go func(ctx context.Context) {    defer wg.Done()    defer cancel()    // Other server,such as:    // UDPServer(ctx)}(ctx)

这个程序实际上包含了几条通道:

如果需要主动退出,通知所有Listener做清理然后退出,可以在parent goroutine调用cancel。上面是在任意goroutine退出后,通知所有goroutine退出。 如果某个sub-goroutine需要通知其他sub-goroutine退出,不应该直接调用cancel方法,而是通过chan(上面是quit)在goroutine返回时告知parent goroutine,由parent goroutine处理。 在sub-goroutine中,如果还需要启动goroutine,可以新开context做同步。如果是可以直接用select,那就可以不用新开goroutine,而是直接select ctx,等待退出。一般而言,goroutine的退出,就意味着一个退出的事件,而这个事件应该由parent goroutine处理,而不能直接广播给其他goroutine。 chan的读写,参考读取 chan和写入chan。

特别是对于library,在参数中支持context,是非常重要的一个要素,这样可以收取到user要求退出或cancel的信号。

总结

以上是内存溢出为你收集整理的GOLANG的context和并发模型全部内容,希望文章能够帮你解决GOLANG的context和并发模型所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1278422.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-09
下一篇 2022-06-09

发表评论

登录后才能评论

评论列表(0条)

保存