ๅ ็ฎๅ็ไบ่งฃไธไธๅ ็ง ctx:
- emptyCtx๏ผๆๆ ctx ็ฑปๅ็ๆ น๏ผ็จ context.TODO()๏ผๆ context.Background() ๆฅ็ๆใ
- valueCtx๏ผไธป่ฆๅฐฑๆฏไธบไบๅจ ctx ไธญๅตๅ ฅไธไธๆๆฐๆฎ๏ผไธไธช็ฎๅ็ k ๅ v ็ปๆ๏ผๅไธไธช ctx ๅ ๅชๆฏๆไธๅฏน kv๏ผ้่ฆๆดๅค็ kv ็่ฏ๏ผไผๅฝขๆไธๆฃตๆ ๅฝข็ปๆใ
- cancelCtx๏ผ็จๆฅๅๆถ็จๅบ็ๆง่กๆ ๏ผไธ่ฌ็จ WithCancel๏ผWithTimeout๏ผWithDeadline ่ฟๅ็ๅๆถๅฝๆฐๆฌ่ดจไธ้ฝๆฏๅฏนๅบไบ cancelCtxใ
- timerCtx๏ผๅจ cancelCtx ไธๅ ไบไธๅฑ๏ผๆฏๆๅบไบๆถ้ด็ cancelใ
็จๆฅๅฎ็ฐ context.TODO() ๅ context.Background()๏ผไธ่ฌๆฏๆๆ context ๆ ็ๆ นใ
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
todo ๅ background ไธค่ ๆฌ่ดจไธๅชๆๅๅญๅบๅซ๏ผๅจๆ string ่พๅบ็ๆถๅไผๆๅบๅซใ
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
package main
import (
"context"
"fmt"
)
type orderID int
func main() {
var x = context.TODO()
x = context.WithValue(x, orderID(1), "1234")
x = context.WithValue(x, orderID(2), "2345")
x = context.WithValue(x, orderID(3), "3456")
fmt.Println(x.Value(orderID(2)))
}
่ฟๆ ท็ไปฃ็ ไผ็ๆไธ้ข่ฟๆ ท็ๆ ๏ผ
โโโโโโโโโโโโโโ
โ emptyCtx โ
โโโโโโโโโโโโโโ
โฒ
โ
โ
โ parent
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 1, v: 1234} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ
โ
โ parent
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 2, v: 2345} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ
โ parent
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 3, v: 3456} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
็ฎๅๆนไธไธไปฃ็ ๏ผ่ฎฉ็ปๆๆดๅไธๆฃตๆ ๏ผ
package main
import (
"context"
"fmt"
)
type orderID int
func main() {
var x = context.TODO()
x = context.WithValue(x, orderID(1), "1234")
x = context.WithValue(x, orderID(2), "2345")
y := context.WithValue(x, orderID(3), "4567")
x = context.WithValue(x, orderID(3), "3456")
fmt.Println(x.Value(orderID(3)))
fmt.Println(y.Value(orderID(3)))
}
ๅฐฑๆฏๅไธ้ข่ฟๆ ท็ๅพไบ๏ผ
โโโโโโโโโโโโโโ
โ emptyCtx โ
โโโโโโโโโโโโโโ
โฒ
โ
โ
โ parent
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 1, v: 1234} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ
โ
โ parent
โ
โ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 2, v: 2345} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ valueCtx{k: 3, v: 3456} โ โ valueCtx{k: 3, v: 4567} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโ โโโโโโโโโ
โ x โ โ y โ
โโโโโโโโโ โโโโโโโโโ
valueCtx ไธป่ฆๅฐฑๆฏ็จๆฅๆบๅธฆ่ดฏ็ฉฟๆดไธช้ป่พๆต็จ็ๆฐๆฎ็๏ผๅจๅๅธๅผ็ณป็ปไธญๆๅธธ่ง็ๅฐฑๆฏ trace_id๏ผๅจไธไบไธๅก็ณป็ปไธญ๏ผไธไบไธๅกๆฐๆฎ้กนไน้่ฆ่ดฏ็ฉฟๆดไธช่ฏทๆฑ็็ๅฝๅจๆ๏ผๅฆ order_id๏ผpayment_id ็ญใ
WithValue ๆถๅณไผ็ๆ valueCtx๏ผ
func WithValue(parent Context, key, val interface{}) Context {
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
key ๅฟ ้กปไธบ้็ฉบ๏ผไธๅฏๆฏ่พใ
ๅจๆฅๆพๅผ๏ผๅณๆง่ก Value ๆไฝๆถ๏ผไผๅ ๅคๆญๅฝๅ่็น็ k ๆฏไธๆฏ็ญไบ็จๆท็่พๅ ฅ k๏ผๅฆๆ็ธ็ญ๏ผ่ฟๅ็ปๆ๏ผๅฆๆไธ็ญ๏ผไผไพๆฌกๅไธไปๅญ่็นๅ็ถ่็น๏ผไธ็ดๆฅๆพๅฐๆดไธช ctx ็ๆ นใๆฒกๆๆพๅฐ่ฟๅ nilใๆฏไธไธช้ๅฝๆต็จ๏ผ
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key) // ่ฟ้ๅ็ไบ้ๅฝ๏ผc.Context ๅฐฑๆฏ c.parent
}
้่ฟๅๆ๏ผctx ่ฟไน่ฎพ่ฎกๆฏไธบไบ่ฝ่ฎฉไปฃ็ ๆฏๆง่กๅฐไธไธช็น้ฝๅฏไปฅๆ นๆฎๅฝๅๆ ๅตๅตๅ ฅๆฐ็ไธไธๆไฟกๆฏ๏ผไฝๆไปฌไนๅฏไปฅ็ๅฐ๏ผๅฆๆๆไปฌๆฏๆฌกๅ ไธไธชๆฐๅผ้ฝๆง่ก WithValue ไผๅฏผ่ด ctx ็ๆ ็ๅฑๆฐ่ฟ้ซ๏ผๆฅๆพๆๆฌๆฏ่พ้ซ O(H)ใ
ๅพๅคไธๅกๅบๆฏไธญ๏ผๆไปฌๅธๆๅจ่ฏทๆฑๅ ฅๅฃๅญๅ ฅๅผ๏ผๅจ่ฏทๆฑ่ฟ็จไธญ้ๆถๅ็จใ่ฟๆถๅๆไปฌๅฏไปฅๅฐ value ไฝไธบไธไธช map ๆดไฝๅญๅ ฅใ
context.WithValue(context.Background(), info,
map[string]string{"order_id" : "111", "payment_id" : "222"}
)
ๅจๆฒกๆ ctx ็ๆถไปฃ๏ผๆไปฌๆณ่ฆๅๆถไธไธช go ๅบๅป็ๅ็จๆฏๅพ้พ็๏ผๅ ไธบ go func() ่ฟไธชๆไฝๆฒกๆไปปไฝ่ฟๅ๏ผๆไปฅๆไปฌไนๆฒกๆๅๆณๅป่ท่ธช่ฟไนไธไธชๆฐๅๅปบ็ goroutineใ
ๆไบ cancelCtx ไนๅ๏ผๆไปฌๆณ่ฆๅๆถๅฐฑๆฏ่พ็ฎๅไบ๏ผ
package main
import (
"context"
"fmt"
"time"
)
func main() {
jobChan := make(chan struct{})
ctx, cancelFn := context.WithCancel(context.TODO())
worker := func() {
jobLoop:
for {
select {
case <-jobChan:
fmt.Println("do my job")
case <-ctx.Done():
fmt.Println("parent call me to quit")
break jobLoop
}
}
}
// start worker
go worker()
// stop all worker
cancelFn()
time.Sleep(time.Second)
}
ไธ่ฟๅๆถๆไฝไธๅฎๆฏ้่ฆ fork ๅบ็ goroutine ๆฌ่บซ่ฆๅไธไบ้ ๅๅจไฝ็๏ผ
select {
case <-jobChan:
// do my job
fmt.Println("do my job")
case <-ctx.Done():
// parent want me to quit
fmt.Println("parent call me to quit")
break jobLoop
}
่ฟ้ๆไปฌไธ่พนๆถ่ดน่ชๅทฑ็ job channel๏ผไธ่พน่ฟ้่ฆ็ๅฌ ctx.Done()๏ผๅฆๆไธ็ๅฌ ctx.Done()๏ผ้ฃๆพ็ถไนๅฐฑไธ็ฅ้ไปไนๆถๅ้่ฆ้ๅบไบใ
// 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
}
ไฝฟ็จ WithCancel ๅฏไปฅๅพๅฐไธไธช cancelCtx:
// 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) }
}
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // ่ฏดๆ็ถ่็นไธๅฎๆฏ emptyCtx๏ผๆ่
็จๆท่ชๅทฑๅฎ็ฐ็ context.Context
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// cancel ๅ็็ๆถๅ๏ผerr ๅญๆฎตไธๅฎไผ่ขซ่ตๅผ๏ผ่ฟ้่ฏดๆ็ถ่็นๅทฒ็ป่ขซ่ตๅผไบ
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{} // ๆๅฝๅ cancelCtx ่ฟฝๅ ๅฐ็ถ่็นๅป
}
p.mu.Unlock()
} else { // ๅฆๆ็จๆทๆ context ๅ
ๅจไบ่ชๅทฑ็ struct ๅ
ๅฐฑไผๅฐ่ฟไธชๅๆฏใ
go func() {
select {
case <-parent.Done(): // ็ถ่็นๅๆถ๏ผ้่ฆๅฐ่ฟไธชๅๆถๆไปคๅๆญฅ็ปๅญ่็น
child.cancel(false, parent.Err())
case <-child.Done(): // ๅญ่็นๅๆถ็่ฏ๏ผๅฐฑไธ็จ็ญ็ถ่็นไบ
}
}()
}
}
parentCancelCtx ๅช่ฏๅซ context ๅ ๅ ็ไธ็ง็ฑปๅ๏ผๅฆๆ็จๆท่ชๅทฑ็็ฑปๅฎ็ฐไบ context.Context ๆฅๅฃ๏ผๆ่ ๆ ctx ๅ ๅจไบ่ชๅทฑ็็ฑปๅๅ ๏ผๆ่ ๆฏ emptyCtx๏ผ้ฃ่ฟ้ๅง็ป่ฟๅ็ๆฏ nil๏ผfalseใ
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c, true
case *timerCtx:
return &c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
return nil, false
}
}
}
็จ WithDeadline ๅ WithTimeout ้ฝๅฏไปฅ็ๆไธไธช timerCtx:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
}
่ฟๆฏไปๅฎๆน็ example ้ๆๅบๆฅ็ไพๅญ๏ผWithTimeout ๅ ถๅฎๅบๅฑๆฏ็จ WithDeadline ๅฎ็ฐ็ใ
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
็จ WithTimeout ๅ WithDeadline ้ฝไผ็ๆไธไธช timerCtxใ
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// ๅฆๆ็ถ่็น็ dealine ๆด้ ๅ๏ผ้ฃๅฝ็ถไปฅ็ถ่็น็ไธบๅ๏ผๅฝๅ่็น็ deadline ๅฏไปฅๆๅผ
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: d,
}
// ๅไธๅๆณก๏ผๆๅฝๅ่็น็ cancel ๅฝๆฐๅ
ณ่ๅฐ็ถ cancelCtx ่็นไธ
propagateCancel(parent, c)
dur := time.Until(d)
if dur <= 0 {
c.cancel(true, DeadlineExceeded) // ๅทฒ็ป่ถ
ๆถไบ๏ผ้ๅบๅง
return c, func() { c.cancel(false, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil { // ่ฏดๆ็ถ่็นๅฐ็ฐๅจ่ฟๆฒกๆๅๆถๅข
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded) // ่ฟไธชๆนๆณๅฐๆถ้ดไบไนๅไผ่ชๅจๆง่ก๏ผๅฝๅ็ goroutine ไธไผ่ขซ้ปๅก
})
}
return c, func() { c.cancel(true, Canceled) }
}
- ๆฏๆฌกๆง่ก้ฝไผๅๅปบๆฐ็ timer
- ๅญ่็น็ deadline ไธๅฎไธไผ่ถ ่ฟ็ถ่็น
- ๅๅปบ่ฟ็จไธญๅ็ฐๅทฒ็ป่ฟๆไบ๏ผ็ซๅป่ฟๅ
ไธบไปไน่ฎพ่ฎกๆๆ ๅฝข็ปๆๅขใๅ ไธบๅฏนไบ fork-join ็ๆจกๅ(Go ็ๅๅฐ go func ๅฐฑๆฏ่ฟ็งๆจกๅ)ๆฅ่ฏด๏ผ็จๅบไปฃ็ ็ๆง่กๆฌๆฅๅฐฑๆฏๆ ๅฝข็ใๅจ่ฟๅ ฅใ้ๅบๆไธชๅญ่็น็ๆถๅ๏ผๆข่ฆๅ ๆฐ็ๆฐๆฎ๏ผๅไธ่ฝๅฝฑๅ็ถ่็น็ๆฐๆฎ๏ผๆไปฅ่ฟ็ง้พๅผๆ ๅฝข็ปๆๅฏไปฅๅฎ็พๅฐๅน้ ใ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโถโโโ โ
โ โ โ โ โฒ โ
โ โ โ โ โ โ
โ โ โ โ โโโโโโโโโโโโโโโซ โ
โ โ โ โ โ โ โ
โfunc() { โโโโโโโโโโโโโฌโโโโฌโโ โ โโโโโโโ โ
โ โ โ โ โ โ
โ โ โ โโโ โ โ
โ go func1(){}() โโโโโโฌโโโโฌโโโโโโถโโโ โ โ
โ โ โ โ โ
โ โ โ โ โ
โ โ โ โโโ โ
โ go func2(){โโโโโโโโโโฌโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโถโโโ โ
โ โ โ โฒ โ
โ โ โ โ โ
โ โ โ โ โ
โ go func3(){}()โโโฌโโโโฌโโโโโโโโโโ โ โ
โ โ โ โ โ โ
โ โ โ โ โ โ
โ โ โ โ โโโ โ
โ }() โ โ โโโโโโโโโโโโโโโโโถโโโ โ
โ} โ โ โ
โ โ โ โ
โ โ โ โ
โ โ โ โ
โ โ โ โ
โ โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๅๆถๆไธช็ถ่็น๏ผๅ่ฝๅค็จๆ ็้ๅ็ฎๆณๅฐ่ฏฅๅๆถๆไปคไผ ๅฏผๅฐๆดๆฃตๆ ๅปใ
โโโโโโโโโโโโ
โ emptyCtx โ
โโโโโโโโโโโโค
โcancelCtx โ
โโโโโโโโโโโโ
โฒ
โ
โ
โ
โ
โ
โโโโโโโโโโโโ
โcancelCtx โ cancel here
โโโโโโโโโโโโ
โฒ โ
โ โ
โ โ
โโโโโโโโโโโโโโดโโโโโโโโโโโโโโ โ
โ โ โ
โ โขโขโขโขโขโขโขโขโขโขโขโขโข โ
โ โขโขโขโขโข โ โขโขโขโขโข โ
โ โขโขโขโข โ โขโขโขโข โ
โโโโโโโโโโโโ โขโขโขโข โโโโโโโโโโโโ โขโขโข โ
โcancelCtx โ โขโข โcancelCtx โโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโ โขโข โโโโโโโโโโโโ โขโข
โขโข โฒ โขโข
โข โโโโโโโโโโดโโโโโฌโโโโโโโโโโโโโ โขโข
โข โ โ โ โข
โขโข โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โข
โข โcancelCtx โ โcancelCtx โ โcancelCtx โ โข
โข โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโขโขโข
โข โขโขโขโข
โขโข โขโขโขโขโข
โข โขโขโขโขโข
โขโขโข โขโขโขโข
โขโขโขโขโขโข โขโขโขโขโขโข
โขโขโขโขโขโขโขโขโขโขโขโข
ๅฆๅพ๏ผๆ ไธๆไธช่็น cancel ไนๅ๏ผไผ้กบไพฟ่ฐ็จๅ ถ children ๆฐ็ปไธญๆๆๅญ่็น็ๅๆถๅฝๆฐ๏ผ่ฏฅๅๆถๆไฝไธ็ดไผ ๅฏผๅฐๅถๅญ่็นใ
ๅฆๆไธ้่ฟ WithCancel ๆฅๅคๅถ้็ฅ channel๏ผๅคงๅฎถ้ฝไฝฟ็จๅไธไธช ctx.Done๏ผ้ฃไนๅฎ้ ไธๆฏๅจไบไธๆๅคง้ใ
package main
import "context"
import "time"
func main() {
ctx, _ := context.WithCancel(context.TODO())
for i := 0; i < 100; i++ {
go func() {
select {
case <-ctx.Done():
}
}()
}
time.Sleep(time.Hour)
}
ๅจไธไบๅบๆฏๅฏ่ฝไผๆๆง่ฝ้ฎ้ขใ
http ไธญ็ ctx ่ฟๅกไบ map๏ผๆๅฐไผ้ ๆ fatalใ
package main
import (
"fmt"
"net/http"
"reflect"
)
func panic(w http.ResponseWriter, r *http.Request) {
server := r.Context().Value(http.ServerContextKey).(*http.Server)
v := reflect.ValueOf(*server)
for i := 0; i < v.NumField(); i++ {
if name := v.Type().Field(i).Name; name != "activeConn" {
continue
}
fmt.Println(v.Field(i))
}
}
func main() {
http.HandleFunc("/", panic)
err := http.ListenAndServe(":9090", nil)
if err != nil {
fmt.Println(err)
}
}
ctx ็็ปๆๆพ็ถๆฏๆ นๆฎไปฃ็ ็ๆง่กๆจกๅๆฅ่ฎพ่ฎก็๏ผ่ฝ็ถ่ฎพ่ฎกๅพๆฏ่พๅทงๅฆ๏ผไฝๅ ไธบๅฐๅๆถๅไธไธๆๆบๅธฆๅ่ฝๆททๅๅจไธ่ตท๏ผๅจไธไบๆ ๅตไธ่ฟๆฏไผ็ปๆไปฌๅไบๆฏ่พ้่ฝ็ๅใไฝฟ็จๆถ้่ฆๅคๅคๆณจๆใ