Context学习

 

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))
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值