golang使用context作为goroutine之间的控制器,举例:
package main
import (
"context"
"log"
"time"
)
func UseContext(ctx context.Context) {
for {
select {
case <-ctx.Done(): // 从ctx.Done()中读取传递进来的终结信号
log.Printf("context is done with error %s", ctx.Err())
return
default:
log.Printf("nothing just loop...")
time.Sleep(time.Second * time.Duration(1))
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go UseContext(ctx) // 启动一个goroutine,循环打印
time.Sleep(time.Second * time.Duration(1))
cancel() // 关闭goroutine
time.Sleep(time.Second * time.Duration(2))
}
首先看下context是个什么东东
type Context interface {
Deadline() (deadline time.Time, ok bool) // Deadline() 返回上下文完成工作的时间
Done() <-chan struct{} // 空struct的chan
Err() error
Value(key interface{}) interface{}
}
既然是接口,就找一个具体实现
// background返回的是一个实现了 Context接口全部方法 的空方法的context
func Background() Context {
return background
}
// but what is background? it's:
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
type emptyCtx int
// 下面是实现context接口的4个方法
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
Background()既然是一个实现了ctx接口全部方法的空实例,那么context.WithCancel()是一个什么东东
// 主实现逻辑,传入一个context,返回一个context和一个CancelFunc(取消函数)
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent) // 初始化一个取消函数
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// 初始化一个cancelCtx,context等于传进来的
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
}
type cancelCtx struct {
Context
mu sync.Mutex // 空值
done chan struct{} // 空值
children map[canceler]struct{} // 空值
err error // 空值
}
上面可以看出done其实是一个空结构体的chan,接下来看是如何把信号传递给done这个chan的,下面看下cancel()(取消函数)
// 取消关闭 c.done,消去c的每个子结点,
// 如果 removeFromParent为真,从父类的子类中移除c。
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
if c.done == nil {
c.done = closedchan
} else {
close(c.done) // 调用c.done
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
这里调用cancel的时候就是调用了 close(c.done)