欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

GO語言Context的作用及各種使用方法

 更新時(shí)間:2024年01月15日 10:32:10   作者:過去日記  
golang的Context包是專門用來處理多個(gè)goroutine之間與請(qǐng)求域的數(shù)據(jù)、取消信號(hào)、截止時(shí)間等相關(guān)操作,下面這篇文章主要給大家介紹了關(guān)于GO語言Context的作用及各種使用方法的相關(guān)資料,需要的朋友可以參考下

Context

為什么需要Context

Go語言需要Context主要是為了在并發(fā)環(huán)境中有效地管理請(qǐng)求的上下文信息。Context提供了在函數(shù)之間傳遞取消信號(hào)、超時(shí)、截止時(shí)間等元數(shù)據(jù)的一種標(biāo)準(zhǔn)方式。

原因

  • 取消操作: 在并發(fā)環(huán)境中,當(dāng)一個(gè)請(qǐng)求被取消或者超時(shí)時(shí),需要有效地通知相關(guān)的協(xié)程停止正在進(jìn)行的工作。使用Context可以通過傳遞取消信號(hào)來實(shí)現(xiàn)這一點(diǎn)。
  • 超時(shí)控制: 在一些場(chǎng)景下,限制操作執(zhí)行的時(shí)間是很重要的。Context提供了一個(gè)統(tǒng)一的方式來處理超時(shí),確保在規(guī)定的時(shí)間內(nèi)完成操作,防止程序無限期地等待。
  • 傳遞上下文信息: Context可以用于傳遞請(qǐng)求的元數(shù)據(jù),例如請(qǐng)求的ID、用戶信息等。這在跨多個(gè)函數(shù)調(diào)用的情況下非常有用,避免了在函數(shù)參數(shù)中傳遞大量的上下文信息。
  • 協(xié)程之間的通信: Go語言中的協(xié)程(goroutine)是輕量級(jí)的線程,它們之間需要有效地通信。Context提供了一個(gè)標(biāo)準(zhǔn)的方式來傳遞信號(hào)和元數(shù)據(jù),以便協(xié)程之間協(xié)同工作。
  • 資源管理: 在一些場(chǎng)景下,需要確保在函數(shù)執(zhí)行完畢后釋放相關(guān)的資源,不管函數(shù)是正常執(zhí)行還是因?yàn)槿∠虺瑫r(shí)而提前退出。Context可以幫助在正確的時(shí)機(jī)釋放資源。
  • 綜上所述,Context是Go語言中處理并發(fā)、超時(shí)和取消等問題的一種優(yōu)雅而一致的方式,使得代碼更加健壯、可維護(hù),并且更容易在不同的并發(fā)場(chǎng)景中工作。

多任務(wù)超時(shí)例子

我們都知道在go語言并發(fā)編程中,我們可以采用select來監(jiān)聽協(xié)程的的通道控制協(xié)程,但是如下面的這種情況僅僅憑借select就顯得有些無能為力:

  • 支持多級(jí)嵌套,父任務(wù)停止后,子任務(wù)自動(dòng)停止
  • 控制停止順序,先停EFG 再停BCD 最后停A

目標(biāo)1還好說,目標(biāo)2好像就沒那么靈活了,正式討論context如何解決這些問題前,我們先看下常規(guī)context的使用

Context結(jié)構(gòu)

context 包是 Go 語言中用于處理請(qǐng)求的上下文的標(biāo)準(zhǔn)庫之一。它提供了一種在函數(shù)之間傳遞取消信號(hào)、超時(shí)和截止時(shí)間的機(jī)制。

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}
  • Deadline()返回一個(gè)完成工作的截止時(shí)間,表示上下文應(yīng)該被取消的時(shí)間。如果 ok==false 表示沒有設(shè)置截止時(shí)間。

  • Done()返回一個(gè) Channel,這個(gè) Channel 會(huì)在當(dāng)前工作完成時(shí)被關(guān)閉,表示上下文應(yīng)該被取消。如果無法取消此上下文,則 Done 可能返回 nil。多次調(diào)用 Done 方法會(huì)返回同一個(gè) Channel。

  • Err()返回 Context 結(jié)束的原因,它只會(huì)在 Done 方法對(duì)應(yīng)的 Channel 關(guān)閉時(shí)返回非空值。如果 Context 被取消,會(huì)返回context.Canceled 錯(cuò)誤;如果 Context 超時(shí),會(huì)返回context.DeadlineExceeded錯(cuò)誤。

  • Value()從 Context 中獲取鍵對(duì)應(yīng)的值。如果未設(shè)置 key 對(duì)應(yīng)的值則返回 nil。以相同 key 多次調(diào)用會(huì)返回相同的結(jié)果。

另外,context 包中提供了兩個(gè)創(chuàng)建默認(rèn)上下文的函數(shù):

// TODO 返回一個(gè)非 nil 但空的上下文。
// 當(dāng)不清楚要使用哪種上下文或無可用上下文尚應(yīng)使用 context.TODO。
func TODO() Context

// Background 返回一個(gè)非 nil 但空的上下文。
// 它不會(huì)被 cancel,沒有值,也沒有截止時(shí)間。它通常由 main 函數(shù)、初始化和測(cè)試使用,并作為處理請(qǐng)求的頂級(jí)上下文。
func Background() Context

還有四個(gè)基于父級(jí)創(chuàng)建不同類型上下文的函數(shù):

// WithCancel 基于父級(jí)創(chuàng)建一個(gè)具有 Done channel 的 context
func WithCancel(parent Context) (Context, CancelFunc)

// WithDeadline 基于父級(jí)創(chuàng)建一個(gè)不晚于 d 結(jié)束的 context
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

// WithTimeout 等同于 WithDeadline(parent, time.Now().Add(timeout))
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

// WithValue 基于父級(jí)創(chuàng)建一個(gè)包含指定 key 和 value 的 context
func WithValue(parent Context, key, val interface{}) Context

在后面會(huì)詳細(xì)介紹這些不同類型 context 的用法。

Context各種使用方法

創(chuàng)建context

context包主要提供了兩種方式創(chuàng)建context:

  • context.Backgroud()
  • context.TODO()

這兩個(gè)函數(shù)其實(shí)只是互為別名,沒有差別,官方給的定義是:

  • context.Background 是上下文的默認(rèn)值,所有其他的上下文都應(yīng)該從它衍生(Derived)出來。
  • context.TODO 應(yīng)該只在不確定應(yīng)該使用哪種上下文時(shí)使用;
    所以在大多數(shù)情況下,我們都使用context.Background作為起始的上下文向下傳遞。

上面的兩種方式是創(chuàng)建根context,不具備任何功能,具體實(shí)踐還是要依靠context包提供的With系列函數(shù)來進(jìn)行派生:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

valueCtx

valueCtx結(jié)構(gòu)體

type valueCtx struct {
    Context
    key, val interface{}
}

func (c *valueCtx) Value(key interface{}) interface{} {
    if c.key == key {
        return c.val
    }
    return c.Context.Value(key)
}

valueCtx利用一個(gè)Context類型的變量來表示父節(jié)點(diǎn)context,所以當(dāng)前context繼承了父context的所有信息;valueCtx類型還攜帶一組鍵值對(duì),也就是說這種context可以攜帶額外的信息。valueCtx實(shí)現(xiàn)了Value方法,用以在context鏈路上獲取key對(duì)應(yīng)的值,如果當(dāng)前context上不存在需要的key,會(huì)沿著context鏈向上尋找key對(duì)應(yīng)的值,直到根節(jié)點(diǎn)。

WithValue

我們?nèi)粘T跇I(yè)務(wù)開發(fā)中都希望能有一個(gè)trace_id能串聯(lián)所有的日志,這就需要我們打印日志時(shí)能夠獲取到這個(gè)trace_id,在python中我們可以用gevent.local來傳遞,在java中我們可以用ThreadLocal來傳遞,在Go語言中我們就可以使用Context來傳遞,通過使用WithValue來創(chuàng)建一個(gè)攜帶trace_id的context,然后不斷透?jìng)飨氯?,打印日志時(shí)輸出即可,來看使用例子:

package main

import (
	"context"
	"fmt" // 我們需要使用fmt包中的Println()函數(shù)
	"strings"
	"time"

	"github.com/google/uuid"
)

const (
	KEY = "trace_id"
)

// 生成隨機(jī)ID
func NewRequestID() string {
	return strings.Replace(uuid.New().String(), "-", "", -1)
}
// 生成攜帶值的context
func NewContextWithTraceID() context.Context {
	ctx := context.WithValue(context.Background(), KEY, NewRequestID())
	return ctx
}
//打印數(shù)據(jù)
func PrintLog(ctx context.Context, message string) {
	fmt.Printf("%s|info|trace_id=%s|%s", time.Now().Format("2006-01-02 15:04:05"), GetContextValue(ctx, KEY), message)
}
// 獲取context中的值
func GetContextValue(ctx context.Context, k string) string {
	v, ok := ctx.Value(k).(string)
	if !ok {
		return ""
	}
	return v
}

func ProcessEnter(ctx context.Context) {
	PrintLog(ctx, "Golang夢(mèng)工廠")
}

func main() {
	ProcessEnter(NewContextWithTraceID())
}

結(jié)果

2024-01-10 18:55:03|info|trace_id=c4eeb76d427449fda52a4775ccbc0509|Golang夢(mèng)工廠

cancelCtx

cancelCtx結(jié)構(gòu)體

type cancelCtx struct {
    Context

    mu       sync.Mutex            // 同步鎖,保護(hù)下面的所有字段
    done     chan struct{}         //惰性創(chuàng)建,由第一次取消調(diào)用關(guān)閉
    children map[canceler]struct{} // 在第一次取消調(diào)用時(shí),設(shè)置為 nil
    err      error                 // 在第一次取消調(diào)用時(shí)設(shè)置為 non-nil 
}

type canceler interface {
    cancel(removeFromParent bool, err error)
    Done() <-chan struct{}
}

跟valueCtx類似,cancelCtx中也有一個(gè)context變量作為父節(jié)點(diǎn);變量done表示一個(gè)channel,用來表示傳遞關(guān)閉信號(hào);children表示一個(gè)map,存儲(chǔ)了當(dāng)前context節(jié)點(diǎn)下的子節(jié)點(diǎn);err用于存儲(chǔ)錯(cuò)誤信息表示任務(wù)結(jié)束的原因。

withCancel

日常業(yè)務(wù)開發(fā)中我們往往為了完成一個(gè)復(fù)雜的需求會(huì)開多個(gè)gouroutine去做一些事情,這就導(dǎo)致我們會(huì)在一次請(qǐng)求中開了多個(gè)goroutine確無法控制他們,這時(shí)我們就可以使用withCancel來衍生一個(gè)context傳遞到不同的goroutine中,當(dāng)我想讓這些goroutine停止運(yùn)行,就可以調(diào)用cancel來進(jìn)行取消。

package main

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

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	go Speak(ctx)
	time.Sleep(10 * time.Second)
	cancel()
	time.Sleep(1 * time.Second)
}

func Speak(ctx context.Context) {
	for range time.Tick(time.Second) {
		select {
		case <-ctx.Done():
			fmt.Println("我要閉嘴了")
			return
		default:
			fmt.Println("balabalabalabala")
		}
	}
}

timerCtx

timerCtx是一種基于cancelCtx的context類型,從字面上就能看出,這是一種可以定時(shí)取消的context。

type timerCtx struct {
    cancelCtx
    timer *time.Timer // Under cancelCtx.mu.

    deadline time.Time
}

func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
    return c.deadline, true
}

func (c *timerCtx) cancel(removeFromParent bool, err error) {
    //將內(nèi)部的cancelCtx取消
    c.cancelCtx.cancel(false, err)
    if removeFromParent {
        // Remove this timerCtx from its parent cancelCtx's children.
        removeChild(c.cancelCtx.Context, c)
    }
    c.mu.Lock()
    if c.timer != nil {
        取消計(jì)時(shí)器
        c.timer.Stop()
        c.timer = nil
    }
    c.mu.Unlock()
}

timerCtx內(nèi)部使用cancelCtx實(shí)現(xiàn)取消,另外使用定時(shí)器timer和過期時(shí)間deadline實(shí)現(xiàn)定時(shí)取消的功能。timerCtx在調(diào)用cancel方法,會(huì)先將內(nèi)部的cancelCtx取消,如果需要?jiǎng)t將自己從cancelCtx祖先節(jié)點(diǎn)上移除,最后取消計(jì)時(shí)器。

WithDeadline

WithDeadline 用于設(shè)置一個(gè)絕對(duì)時(shí)間,表示在某個(gè)具體的時(shí)間點(diǎn)超時(shí),例如 context.WithDeadline(parentContext, time.Now().Add(10 * time.Second)) 表示在當(dāng)前時(shí)間的 10 秒后超時(shí)。

package main

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

func main() {
	HttpHandler()
}

func NewContextWithTimeout() (context.Context, context.CancelFunc) {
	return context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
}

func HttpHandler() {
	ctx, cancel := NewContextWithTimeout()
	defer cancel()
	deal(ctx)
}

func deal(ctx context.Context) {
	for i := 0; i < 10; i++ {
		time.Sleep(1 * time.Second)
		select {
		case <-ctx.Done():
			fmt.Println(ctx.Err())
			return
		default:
			fmt.Printf("deal time is %d\n", i)
		}
	}
}

WithTimeout

WithTimeout 用于設(shè)置一個(gè)相對(duì)時(shí)間,表示在多長時(shí)間后超時(shí),例如 context.WithTimeout(parentContext, 5 * time.Second) 表示在 5 秒后超時(shí)。

package main

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

func main() {
	HttpHandler()
}

func NewContextWithTimeout() (context.Context, context.CancelFunc) {
	return context.WithTimeout(context.Background(), 3*time.Second)
}

func HttpHandler() {
	ctx, cancel := NewContextWithTimeout()
	defer cancel()
	deal(ctx)
}

func deal(ctx context.Context) {
	for i := 0; i < 10; i++ {
		time.Sleep(1 * time.Second)
		select {
		case <-ctx.Done():
			fmt.Println(ctx.Err())
			return
		default:
			fmt.Printf("deal time is %d\n", i)
		}
	}
}

總結(jié)

context主要用于父子任務(wù)之間的同步取消信號(hào),本質(zhì)上是一種協(xié)程調(diào)度的方式。另外在使用context時(shí)有兩點(diǎn)值得注意:上游任務(wù)僅僅使用context通知下游任務(wù)不再需要,但不會(huì)直接干涉和中斷下游任務(wù)的執(zhí)行,由下游任務(wù)自行決定后續(xù)的處理操作,也就是說context的取消操作是無侵入的;context是線程安全的,因?yàn)閏ontext本身是不可變的(immutable),因此可以放心地在多個(gè)協(xié)程中傳遞使用。

到此這篇關(guān)于GO語言Context的作用及各種使用方法的文章就介紹到這了,更多相關(guān)GO語言Context使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言基礎(chǔ)類型及常量用法示例詳解

    Go語言基礎(chǔ)類型及常量用法示例詳解

    這篇文章主要為大家介紹了Go語言基礎(chǔ)類型及常量的用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • Golang中的內(nèi)存泄漏你真的理解了嗎

    Golang中的內(nèi)存泄漏你真的理解了嗎

    內(nèi)存泄漏是編程中常見的問題,會(huì)對(duì)程序的性能和穩(wěn)定性產(chǎn)生嚴(yán)重影響,本文將深入詳解?Golang?中的內(nèi)存泄漏的原因、檢測(cè)方法以及避免方法,希望對(duì)大家有所幫助
    2023-12-12
  • Go語言中init函數(shù)和defer延遲調(diào)用關(guān)鍵詞詳解

    Go語言中init函數(shù)和defer延遲調(diào)用關(guān)鍵詞詳解

    這篇文章主要介紹了Go語言中init函數(shù)和defer延遲調(diào)用關(guān)鍵詞,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • golang實(shí)現(xiàn)的文件上傳下載小工具

    golang實(shí)現(xiàn)的文件上傳下載小工具

    這篇文章主要介紹了golang實(shí)現(xiàn)的文件上傳下載小工具,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2020-12-12
  • Golang如何編寫內(nèi)存高效及CPU調(diào)優(yōu)的Go結(jié)構(gòu)體

    Golang如何編寫內(nèi)存高效及CPU調(diào)優(yōu)的Go結(jié)構(gòu)體

    這篇文章主要介紹了Golang如何編寫內(nèi)存高效及CPU調(diào)優(yōu)的Go結(jié)構(gòu)體,結(jié)構(gòu)體是包含多個(gè)字段的集合類型,用于將數(shù)據(jù)組合為記錄
    2022-07-07
  • go MethodByName()不能獲取私有方法的解決

    go MethodByName()不能獲取私有方法的解決

    本文主要介紹了go MethodByName()不能獲取私有方法的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • go語言中嵌套結(jié)構(gòu)體的實(shí)現(xiàn)

    go語言中嵌套結(jié)構(gòu)體的實(shí)現(xiàn)

    在Go語言中,嵌套結(jié)構(gòu)體可定義為一個(gè)結(jié)構(gòu)體內(nèi)包含另一個(gè)結(jié)構(gòu)體,嵌套可以是值嵌套或指針嵌套,兩者在內(nèi)存分配和修改影響上有顯著區(qū)別,本文就來詳細(xì)的介紹一下,感興趣的可以了解一下
    2024-09-09
  • Go語言crypto包創(chuàng)建自己的密碼加密工具實(shí)現(xiàn)示例

    Go語言crypto包創(chuàng)建自己的密碼加密工具實(shí)現(xiàn)示例

    Go語言借助它的簡(jiǎn)單性和強(qiáng)大的標(biāo)準(zhǔn)庫,實(shí)現(xiàn)一個(gè)自己的密碼加密工具,本文將會(huì)結(jié)合代碼示例深入探討如何使用Go語言的crypto包來實(shí)現(xiàn)自己的加密工具
    2023-11-11
  • 基于Go語言簡(jiǎn)單實(shí)現(xiàn)事件管理器

    基于Go語言簡(jiǎn)單實(shí)現(xiàn)事件管理器

    在編程中,事件管理器是一種常見的工具,用于通過通知來觸發(fā)操作,本文將介紹一個(gè)簡(jiǎn)單的Go事件管理器的實(shí)現(xiàn),并通過異步改進(jìn)提高其性能,感興趣的可以了解下
    2023-11-11
  • Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)

    Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)

    Go中的?channel?其實(shí)并沒有提供批量讀取數(shù)據(jù)的方法,需要我們自己實(shí)現(xiàn)一個(gè),使用本文就來為大家大家介紹一下如何通過Go?channel實(shí)現(xiàn)批量讀取數(shù)據(jù)吧
    2023-12-12

最新評(píng)論