golang知識點--context

概述

context包定義了context的類型,該類型在api和進程之間傳遞deadline,取消信號和其他請求範圍的值。
向服務器的傳入請求應創建一個上下文,而對服務器的傳出調用應接受一個上下文。 它們之間的函數調用鏈必須傳播Context,可以選擇將其替換爲使用WithCancel,WithDeadline,WithTimeout或WithValue創建的派生Context。 取消上下文後,從該上下文派生的所有上下文也會被取消。
WithCancel,WithDeadline和WithTimeout函數採用Context(父級)並返回派生的Context(子級)和CancelFunc。 調用CancelFunc會取消該子代及其子代,刪除父代對該子代的引用,並停止所有關聯的計時器。 未能調用CancelFunc會使子代及其子代泄漏,直到父代被取消或計時器觸發。 go vet檢查所有控制流路徑上是否都使用了CancelFuncs。
使用上下文的程序應遵循以下規則,以使各個包之間的接口保持一致,並啓用靜態分析工具來檢查上下文傳播:

  1. 不要將上下文存儲在結構類型中; 而是將上下文明確傳遞給需要它的每個函數。 Context應該是第一個參數,通常命名爲ctx:
func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}
  1. 即使函數允許,也不要傳遞nil Context。 如果不確定使用哪個上下文,請傳遞context.TODO。
  2. 僅將上下文值用於傳遞過程和API的請求範圍的數據,而不用於將可選參數傳遞給函數。
  3. 可以將相同的上下文傳遞給在不同goroutine中運行的函數。 上下文對於由多個goroutine同時使用是安全的。

常用函數介紹

1.WithCancel
作用:返回一個父上下文的拷貝,並攜帶一個Done的管道。不論返回的cancel函數被調用或者當父上下文的Done channel關閉這兩種情況哪一個先發生,返回的上下文的Done channel關閉。
取消此context回什邡預期關聯的資源,因此在此上下文中運行的操作完成後,代碼應立即調用cancel。

此示例演示了使用可取消上下文來防止goroutine泄漏。 在示例函數結束時,由gen啓動的goroutine將返回而不會泄漏。

package main

import (
	"context"
	"fmt"
)

func main() {
	// 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 := func(ctx context.Context) <-chan int {
		dst := make(chan int)
		n := 1
		go func() {
			for {
				select {
				case <-ctx.Done():
					return // returning not to leak the goroutine
				case dst <- n:
					n++
				}
			}
		}()
		return dst
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // cancel when we are finished consuming integers

	for n := range gen(ctx) {
		fmt.Println(n)
		if n == 5 {
			break
		}
	}
}

2.WithDeadline
WithDeadline返回父上下文的副本,並將截止日期調整爲不遲於d(d是由我們定義的一個時間點,此處是與WithTimeout區別的地方),當父運行時間少於d,則本質上等於父上下文,當返回調用的cancel函數或者關閉父上下文的Done通道在截止時期之後,則關閉返回的上下文的Done通道。

該示例傳遞一個帶有任意截止日期的上下文,以告知阻塞函數它應在到達工作後立即放棄工作。

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	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
	// cancellation function in any case. Failure to do so may keep the
	// context and its parent alive longer than necessary.
	defer cancel()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("overslept")
	case <-ctx.Done():
		fmt.Println(ctx.Err())
	}

}

3.WithTimeout
WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
這個例子讓阻塞等待50Millisecond後結束

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

}

4.WithValue
WithValue返回父項的副本,其中與鍵關聯的值爲val。
僅將上下文值用於傳遞過程和API的請求範圍的數據,而不用於將可選參數傳遞給函數。
此示例演示如何將值傳遞給上下文,以及如何將其檢索(如果存在)。
提供的密鑰必須具有可比性,並且不能爲字符串類型或任何其他內置類型,以避免使用上下文在程序包之間發生衝突。 WithValue的用戶應定義自己的密鑰類型。 爲了避免在分配給接口{}時分配,上下文鍵通常具有具體類型struct {}。 另外,導出的上下文鍵變量的靜態類型應該是指針或接口。

package main

import (
	"context"
	"fmt"
)

func main() {
	type favContextKey string

	f := func(ctx context.Context, k favContextKey) {
		if v := ctx.Value(k); v != nil {
			fmt.Println("found value:", v)
			return
		}
		fmt.Println("key not found:", k)
	}

	k := favContextKey("language")
	ctx := context.WithValue(context.Background(), k, "Go")

	f(ctx, k)
	f(ctx, favContextKey("color"))

}

context在併發模式中的應用

簡介
在Go的服務中,每個傳入請求都在自己的goroutine中進行處理。請求處理程序一般會啓動另外的goroutine來訪問後端,如數據庫和RPC服務。這一系列在request運行的goroutine通常需要權限去訪問特定的值,比如終端用戶的身份,授權令牌,請求期限。當請求被取消或者超時,依賴於該request的所有goroutine應該迅速退出,以便系統可以回收他們正在使用的任何資源。

context包就是用於跨api邊界的將request範圍的值,取消信號,和截止時間傳遞給所有涉及處理request的值。該軟件包可作爲上下文公開使用。
關於context的類型:

// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

上下文對於由多個goroutine同時使用是安全的。 代碼可以將單個上下文傳遞給任意數量的goroutine,並取消該上下文以發出所有信號。
派生上下文
上下文包提供了從現有值派生新的Context值的功能。 這些值形成一棵樹:取消上下文後,從該上下文派生的所有上下文也會被取消。
Background 是任何上下文樹的根; 它永遠不會被取消:
WithCancel和WithTimeout返回派生的Context值,該值可以比父Context早被取消。 請求處理程序返回時,通常會取消與傳入請求關聯的上下文。 使用多個副本時,WithCancel對於取消冗餘請求也很有用。 WithTimeout對於設置對後端服務器的請求的截止日期很有用:
WithValue提供了一種將請求範圍的值與Context相關聯的方法

參考文章:
https://golang.org/pkg/context/
https://blog.golang.org/context

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章