Golang中使用context作为goroutine之间的控制器,例如:
package main
import (
"context"
"log"
"time"
)
func UseContext(ctx context.Context) {
for {
select {
case <-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)
time.Sleep(time.Second * time.Duration(1))
cancel()
time.Sleep(time.Second * time.Duration(2))
}
mainUseContext
context
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
context.Background()
func Background() Context {
return background
}
// but what is background? it's:
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
emptyCtxWithCancel()
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done chan struct{} // created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
UseContextcancel
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
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) // NOTE(jiajun): here it is :)
}
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)
}
}
close(c.done)UseContext
总结
这篇博客里我们看到了context是怎么实现的,读者如果有兴趣的话,可以看看Context中的传值和取值是怎么实现的,以及思考一下 是否有更好的实现方案。