diff --git a/pkg/context.htm b/pkg/context.htm index 3391f5a..e62cfcb 100644 --- a/pkg/context.htm +++ b/pkg/context.htm @@ -20,54 +20,30 @@
import "context"
-
-Package context defines the Context type, which carries deadlines, -cancelation signals, and other request-scoped values across API boundaries -and between processes. -
+context包定义了 "Context" 类型(上下文类型),该类型的主要作用就是用来控制goroutine的生命周期的。这个类型通过触发几种信号来达到通知子goroutine的目的,比如到期终止的信号、取消操作的信号,当子goroutine接收到这些信号之后就可以做出相应的清理工作结束goroutine。此外该类型还可以跨程序单元(多个goroutine之间)传递一些请求相关的变量。
-Incoming requests to a server should create a Context, and outgoing -calls to servers should accept a Context. The chain of function -calls between them must propagate the Context, optionally replacing -it with a derived Context created using WithCancel, WithDeadline, -WithTimeout, or WithValue. When a Context is canceled, all -Contexts derived from it are also canceled. +在应用中使用Context的时候,我们通常在接收请求的goroutine中创建Context变量 A,然后在派生的子goroutine中接收一个Context变量B,注意接收的这个B必须是A的一个副本或者说子Context。产生子Context的方式就是通过WithCancel,WithDeadline,WithTimeout或WithValue这几个方法得到的。如此方式产生Context变量并且传递子Context变量之后,我们就可以达到链式调用Context的目的了,这就意味着当我们在主goroutine中取消了Context的时候,传递到各个子goroutine中的子Context也同样链式的接收到了这个取消操作。假如你的子goroutine中的子Context还按照如上的规则派生出孙子Context到孙子goroutine,也没关系,链式调用的强大之处就在于孙子也接收到了。
--The WithCancel, WithDeadline, and WithTimeout functions take a -Context (the parent) and return a derived Context (the child) and a -CancelFunc. Calling the CancelFunc cancels the child and its -children, removes the parent's reference to the child, and stops -any associated timers. Failing to call the CancelFunc leaks the -child and its children until the parent is canceled or the timer -fires. The go vet tool checks that CancelFuncs are used on all -control-flow paths. +
具体到上面提及的几个派生方法来说,WithCancel/WithDeadline/WithTimeout 函数都需要一个Context变量作为父节点,然后这几个函数会返回一个基于该父Context的子Context以及一个CancelFunc类型的取消函数。当主动调用CancelFunc函数的时候就会取消父Context与子Context以及链条上所有Context之间的关系和定时器,(这句话看着有点蒙,实际调用就是给各个子Context的Done通道发送了一个可读事件)。如果需要终止子Context而又不主动调用取消函数的话,就会产生子Context的泄漏了,只能等到主goroutine结束才能回收。go vet工具可以检查所有流程控制路径上使用CancelFuncs的情况。
-Programs that use Contexts should follow these rules to keep interfaces -consistent across packages and enable static analysis tools to check context -propagation: +使用Context的时候应该遵守以下几个规则,以便保持包接口之间的一致性,方便静态分析工具检查Context的上下文传播情况:
-Do not store Contexts inside a struct type; instead, pass a Context -explicitly to each function that needs it. The Context should be the first -parameter, typically named ctx: +1. 不要将 Contexts 放入结构体,context变量应该作为第一个参数传入,最好命名为ctx。
func DoSomething(ctx context.Context, arg Arg) error { // ... use ctx ... }
-Do not pass a nil Context, even if a function permits it. Pass context.TODO -if you are unsure about which Context to use. +2. 即使函数允许,也不要传入nil的 Context。如果不知道用哪种 Context,可以使用context.TODO()。
-Use context Values only for request-scoped data that transits processes and -APIs, not for passing optional parameters to functions. +3. 使用context的Value相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数
-The same Context may be passed to functions running in different goroutines; -Contexts are safe for simultaneous use by multiple goroutines. +4. 相同的 Context 可以传递给在不同的goroutine;Context 是并发安全的。
See https://blog.golang.org/context for example code for a server that uses @@ -127,44 +103,43 @@
-Canceled is the error returned by Context.Err when the context is canceled. +当一个上下文被取消之后,调用Context.Err()方法会得到 Canceled 错误。
-DeadlineExceeded is the error returned by Context.Err when the context's -deadline passes. +当一个上下文超时之后,调用Context.Err()方法会得到 DeadlineExceeded 错误。
type CancelFunc func()
-A CancelFunc tells an operation to abandon its work. -A CancelFunc does not wait for the work to stop. -After the first call, subsequent calls to a CancelFunc do nothing. +CancelFunc 会告知正在进行的操作放弃执行, 但是CancelFunc并不会等待该操作完全执行完毕。 +在第一次调用之后,对CancelFunc的后续调用将不再执行任何操作。
type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. + + // Deadline返回代表此上下文必须要结束工作时的时间点。 + // 如果时间点已经到达了,但是上下文仍然没有被取消,则该上下文会被强行取消。 + // 未设置截止日期时,Deadline返回ok==false。 + // 连续调用Deadline返回相同的结果。 Deadline() (deadline time.Time, ok bool) - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - // - // WithCancel arranges for Done to be closed when cancel is called; - // WithDeadline arranges for Done to be closed when the deadline - // expires; WithTimeout arranges for Done to be closed when the timeout - // elapses. + // Done() 方法返回一个空结构体类型的通道,如果当前上下文被取消了,该通道就会被关闭了。 + // 如果当前上下文永远不取消的话,Done() 有可能会返回 nil.(没理解!!) + // 连续调用Done方法返回相同的结果。 + // + // WithCancel方法生成的上下文会在cancel取消函数被调用的时候将done通道关闭; + // WithDeadline方法生成的上下文会在截止时间点达到的时候将done通道关闭; + // WithTimeout方法生成的上下文会在超过了timeout时间后将done通道关闭。 // - // Done is provided for use in select statements: + // done通道通畅用在 select 语句中: // - // // Stream generates values with DoSomething and sends them to out - // // until DoSomething returns an error or ctx.Done is closed. + // // Stream方法会使用 DoSomething方法生成一些值, + // // 如果DoSomething没有出错,并且ctx上下文没有被取消的情况下,这些值会被发送到out通道中去 // func Stream(ctx context.Context, out chan<- Value) error { // for { // v, err := DoSomething(ctx) @@ -179,35 +154,31 @@type Done
() <-chan struct{} - // Err returns a non-nil error value after Done is closed. Err returns - // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. - // After Done is closed, successive calls to Err return the same value. + + // 如果Done通道已经被关闭了,调用上下文的Err方法会返回一个非nil的错误: + // 1. 如果上下文是被Cancel函数取消的,会返回 Canceled 错误 + // 2. 如果上下文是因为达到了截止时间点而被取消的,会返回 DeadlineExceeded 错误 + // 没有其他类型的错误。在done通道被关闭之后,多次调用该方法返回相同的结果 Err() error - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. + // + // Value方法会根据参数key,返回该上下文通过withValue方法设置的key的value值, + // 如果没有设置就返回nil, 多次调用返回相同结果。 + // + // 使用上下文传递key-value这种模式最好只是传递那些跨进程或者跨API使用的请求相关的数据, + // 不要用这种方式传递函数参数类的值。 // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - // - // A key identifies a specific value in a Context. Functions that wish - // to store values in Context typically allocate a key in a global - // variable then use that key as the argument to context.WithValue and - // Context.Value. A key can be any type that supports equality; - // packages should define keys as an unexported type to avoid - // collisions. + // 参数key确定一个value。一般来说想要把value存储到上下文的程序会定一个全局的key变量, + // 然后把这个变量作为 Context.WithValue 或者 Context.Value 的参数。 + // 这个key可以是任何支持大小比较的类型,为了防止使用冲突,最好在包内定义为非导出的类型。 // // Packages that define a Context key should provide type-safe accessors // for the values stored using that key: // - // // Package user defines a User type that's stored in Contexts. + // // user包定义了一个User类型,我们会将一个该类型的变量存储在context中 // package user // // import "context" @@ -215,32 +186,30 @@type Value
(key interface{}) interface{} }
-A Context carries a deadline, a cancelation signal, and other values across -API boundaries. +综上所述,context会包含截止时间以及一个用来标识上下文被取消的通道,以及一些可以传递的值。
-Context's methods may be called by multiple goroutines simultaneously. +上下文的方法有可能被多个goroutine同时调用。
@@ -252,10 +221,8 @@-Background returns a non-nil, empty Context. It is never canceled, has no -values, and has no deadline. It is typically used by the main function, -initialization, and tests, and as the top-level Context for incoming -requests. +Background方法返回一个非nil的空的上下文。该上下文变量是一个顶级上下文变量,不能被取消, +无法携带值,也没有截止日期。该变量只会被用于主函数、或者初始化或者测试或者接收请求的顶级上下文中
@@ -264,11 +231,10 @@-TODO returns a non-nil, empty Context. Code should use context.TODO when -it's unclear which Context to use or it is not yet available (because the -surrounding function has not yet been extended to accept a Context -parameter). TODO is recognized by static analysis tools that determine -whether Contexts are propagated correctly in a program. +TODO方法返回一个非nil的空上下文。 +在实际代码中使用context.TODO 的时候一般是不清楚要使用哪个上下文 +或它还不可用(因为周围函数尚未扩展到接受上下文参数)。 +TODO方法可以被静态分析工具识别出上下文是否在程序中正确传播。
@@ -277,13 +243,12 @@func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
-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. +WithCancel方法返回一个基于parent上下问的副本,该副本拥有一个Done通道,并且会返回一个可以调用的取消函数。 +当取消函数被调用执行的时候done通道就会被关闭 +或者 当parent上下文的Done通道被关闭的时候,该上下文的done通道也会被关闭。
-Canceling this context releases resources associated with it, so code should -call cancel as soon as the operations running in this Context complete. +取消上下文有利于系统资源的释放,所以调用者应该再使用完上下文的时候尽快的调用该上下文的取消函数。
@@ -294,17 +259,14 @@-This example demonstrates the use of a cancelable context to prevent a -goroutine leak. By the end of the example function, the goroutine started -by gen will return without leaking. +该示例演示了使用一个可取消的上下文来组织goroutine泄漏的方法。 +在该示例方法的最后,由gen衍生出来的goroutine会必然正常返回,而不会发生泄漏。
Code:play -
// gen generates integers in a separate goroutine and -// sends them to the returned channel. -// The callers of gen need to cancel the context once -// they are done consuming generated integers not to leak -// the internal goroutine started by gen. ++// gen函数返回一个通道,该通道会接收一个由gen函数衍生的goroutine不断产生的整数数字 +// gen函数的调用者需要在处理所有生成的整数数字完毕之后取消掉上下文,从而避免由gen生成的goroutine发生泄漏 gen := func(ctx context.Context) <-chan int { dst := make(chan int) n := 1 @@ -312,7 +274,7 @@func // returning not to leak the goroutine + return // 接收到上下文取消的信号的时候在此处返回,从而避免groutine的泄漏 case dst <- n: n++ } @@ -322,7 +284,7 @@
func // cancel when we are finished consuming integers +defer cancel() // 当我们处理完程序的时候主动取消上下文 for n := range gen(ctx) { fmt.Println(n) @@ -345,16 +307,16 @@
func func WithDeadline ¶ Uses
-WithDeadline returns a copy of the parent context with the deadline adjusted -to be no later than d. If the parent's deadline is already earlier than d, -WithDeadline(parent, d) is semantically equivalent to parent. The returned -context's Done channel is closed when the deadline expires, when the returned -cancel function is called, or when the parent context's Done channel is -closed, whichever happens first. +WithDeadline方法同样会返回一个parent上下文的副本上下文,该上下文的有效期不会超过参数deadline设置的时间点。 +如果parent上下文的deadline已经比现在设置的deadline要早的话,该上下文的截止时间就使用parent上下文的截止时间。 +Done通道会以下几种情况下被关闭: +
1. 截止时间到达的时候 +
2. 主动调用了Cancel方法 +
3. parent上下文的done通道被关闭了 +-Canceling this context releases resources associated with it, so code should -call cancel as soon as the operations running in this Context complete. +一旦上下文被用完,尽快取消掉该上下文,会节省系统资源。
@@ -365,17 +327,17 @@func Example
-This example passes a context with a arbitrary deadline to tell a blocking -function that it should abandon its work as soon as it gets to it. +这个示例中我们给你一个阻塞中的函数传递了一个设置了截止时间的上下文, +该函数应该在捕获到上下文被取消了就终止自己的工作。
Code:play
d := time.Now().Add(50 * time.Millisecond) ctx, cancel := context.WithDeadline(context.Background(), d) -// Even though ctx will be expired, it is good practice to call its -// cancelation function in any case. Failure to do so may keep the -// context and its parent alive longer than necessary. +// +即便ctx会应为过期而必然会被取消,我们最好还是使用defer语法主动调用取消方法。 +如果不这么干的话,有可能会导致上下文无谓的存在而占用系统资源。 defer cancel() select { @@ -395,11 +357,10 @@func func WithTimeout ¶ Uses
-WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +WithTimeout 其实就是封装了一下 WithDeadline(parent, time.Now().Add(timeout)).
-Canceling this context releases resources associated with it, so code should -call cancel as soon as the operations running in this Context complete: +一旦上下文被用完,尽快取消掉该上下文,会节省系统资源。
func slowOperationWithTimeout(ctx context.Context) (Result, error) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) @@ -416,13 +377,13 @@func Example
-This example passes a context with a timeout to tell a blocking function that -it should abandon its work after the timeout elapses. +这个示例中我们给你一个阻塞中的函数传递了一个设置了工作时长的上下文, +该函数应该在捕获到上下文被取消了就终止自己的工作。
Code:play -
// 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() @@ -443,21 +404,18 @@func func WithValue ¶ Uses
-WithValue returns a copy of parent in which the value associated with key is -val. +WithValue方法会返回一个parent上下文的副本,该副本中会设置key-value信息。
-Use context Values only for request-scoped data that transits processes and -APIs, not for passing optional parameters to functions. +不要过度的通过上下文来传递key-value, +最好只在那些与请求信息相关的、跨进程API之间的信息传递先关的情景下使用, +坚决避免使用上下文来传递函数的参数。
-The provided key must be comparable and should not be of type -string or any other built-in type to avoid collisions between -packages using context. Users of WithValue should define their own -types for keys. To avoid allocating when assigning to an -interface{}, context keys often have concrete type -struct{}. Alternatively, exported context key variables' static -type should be a pointer or interface. +设置上下文的key-value的时候,key必须是一个可比较的变量, +为了避免包之间的冲突,尽量避免使用内建类型:调用者最好为key自定义一些类型。 +
尽量避免使用万能的interface{}作为key的类型,一定要具体一点。 +或者,可导出的上下文键静态变量的类型应该是指针或接口。