WithCancel实例:
package main
import (
"context"
"fmt"
)
/*
Context 包定义了上下文类型,该上下文类型跨越 API 边界和进程之间传递截止期限,取消信号和其他请求范围值。
对服务器的传入请求应创建一个 Context,对服务器的传出调用应接受 Context。它们之间的函数调用链必须传播 Context,可以用使用 WithCancel,WithDeadline,WithTimeout 或WithValue创建的派生上下文替换。当 Context 被取消时,从它派生的所有 Context 也被取消。
WithCancel,WithDeadline 和 WithTimeout 函数采用Context(父级)并返回派生的Context(子级)和CancelFunc。调用 CancelFunc 将取消子对象及其子对象,删除父对子对象的引用,并停止任何关联的定时器。未能调用CancelFunc 会泄漏子项及其子项,直至父项被取消或计时器激发。go vet 工具检查在所有控制流路径上使用 CancelFuncs。
使用 Contexts 的程序应该遵循这些规则来保持包之间的接口一致,并使静态分析工具能够检查上下文传播:
不要将上下文存储在结构类型中;相反,将一个 Context 明确地传递给每个需要它的函数。上下文应该是第一个参数,通常命名为 ctx
*/
func main() {
// Create a new cancellable context from a new blank context.
//
// 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.
//
// 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.
ctx, cancel := context.WithCancel(context.Background())
// Even though ctx should have been cancelled already, 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.
defer cancel()
// Create a new message channel.
msg := make(chan string)
// Call fnc 10 times in their own goroutines.
for i := 0; i < 10; i++ {
go fnc(i, msg, ctx, cancel)
}
// Receive and print a single message from the msg channel.
fmt.Println(<-msg)
// Wait until the cotext is cancelled before exiting main.
<-ctx.Done()
}
// fnc takes in an integer, message string channel, a context and also a cancel function
// for that context.
func fnc(i int, msg chan string, ctx context.Context, cancel context.CancelFunc) {
select {
// Check if the context was already cancelled.
//
// 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.
case <-ctx.Done():
fmt.Printf("fnc %d cancelled\n", i)
return
// Send a string on the msg channel and then cancel the context so the
// other goroutines exit cleanly.
case msg <- fmt.Sprintf("fnc %d finished first\n", i):
cancel()
}
}
WithTimeout示例:
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
func main() {
// Seed the random number generator.
rand.Seed(time.Now().UnixNano())
// Create 2 random time durations between 0-100 milliseconds.
d1 := time.Duration(rand.Intn(100)) * time.Millisecond
d2 := time.Duration(rand.Intn(100)) * time.Millisecond
// Print out the 2 time durations.
fmt.Printf("d1: %s, d2: %s\n", d1, d2)
// Create a new context with d1 as it's timeout.
//
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// 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.
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
ctx, cancel := context.WithTimeout(context.Background(), d1)
// Even though ctx should have expired already, 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.
defer cancel()
// Print a specified message based on whether the context's deadline has expired yet or not.
select {
case <-ctx.Done():
fmt.Println("timed out before time.After completed")
case <-time.After(d2):
fmt.Println("time.After completed")
}
}
WithValue示例:
package main
import (
"context"
"fmt"
"log"
"net/http"
"strings"
)
func printUsernameHandler(w http.ResponseWriter, r *http.Request) {
// 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.
username := r.Context().Value("username").(string)
if strings.TrimSpace(username) == "" {
http.Error(w, "username is blank", http.StatusBadRequest)
return
}
if username == "admin" {
fmt.Fprintln(w, "Welcome Administrator")
return
}
fmt.Fprintf(w, "Hello, %s\n", strings.ToUpper(username))
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
splitPath := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(splitPath) < 2 || splitPath[0] != "username" {
http.Error(w, "username is not set", http.StatusBadRequest)
return
}
// Create a shallow copy of r with it's context changed to a new context that
// is the same as r's current context but with an added value of a username.
//
// The new request object is then passed to the printUsernameHandler handler function.
//
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The provided key must be comparable.
newReq := r.WithContext(context.WithValue(
r.Context(), "username", splitPath[1],
))
printUsernameHandler(w, newReq)
})
log.Fatal(http.ListenAndServe(":9000", nil))
}