前端之家收集整理的这篇文章主要介绍了
GOLANG的context和并发模型,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
@H_
403_0@
GOLANG1.7新增了context,最初这个package是在
golang.org/x/net/context
中的,后来证实对很多程序都是必须的,就纳入了标准库。
@H_
403_0@对context的介绍是在
context,读这个blog之前,要先读
pipelines(pipelines提出了使用
close(chan)
的方式广播
退出事件)。
@H_
403_0@一般来说,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")
}()
var result1 string
go func(){
defer wg.Done()
res1,err := http.Get("http://server1/api1")
}()
wg.Wait()
w.Write([]byte(result0 + result1)
})
@H_
403_0@实际上这个程序是不能这么写的,如果这两个goroutine请求的时间比较长,让
用户一直等着么?如果
用户取消了请求,
关闭了连接呢?如果
用户指定了超时时间呢?
@H_
403_0@另外,考虑如何
关闭一个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
}
}()
@H_
403_0@我们如何确保goroutine已经
退出后,然后才返回重新开启服务器?如果只是
server.Close
(或者GO1.8之前用
listener.Close
),如何接收外部的事件?如果是goroutine自己的问题,例如端口占用了,如何
通知程序
退出?直接用
os.Exit
明显是太粗鲁的做法。
@H_
403_0@在
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.
@H_
403_0@也就是说,context没有提供Cancel
方法,因为parent goroutine会
调用Cancel,在所有sub goroutines中只需要读
context.Done()
就可以,也就是只是接收
退出信号。
@H_
403_0@还有一处地方,说明了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.
@H_
403_0@只读取chan的好处是,可以使用
close(chan)
方式
通知所有goroutine
退出。
@H_
403_0@使用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)
@H_
403_0@这个程序实际上包含了几条通道:
- 如果需要主动退出,通知所有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。
@H_
403_0@特别是对于library,在参数中
支持context,是非常重要的一个要素,这样可以收取到user要求
退出或cancel的信号。
原文链接:https://www.f2er.com/go/188604.html