Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung des Golang-Kontexts

Detaillierte Erläuterung des Golang-Kontexts

藏色散人
藏色散人nach vorne
2020-09-10 09:30:294012Durchsuche

Die folgende Kolumne enthält eine detaillierte Erklärung des Golang-Kontexts aus der Kolumne „Golang-Tutorial“. Ich hoffe, dass sie für Freunde hilfreich ist, die sie benötigen!

Detaillierte Erläuterung des Golang-Kontexts

VorwortJa, ich wollte heute rausgehen und spielen. Ich kaufte ein Zugticket und verschlafe erneut. . Es gibt keine Möglichkeit, vielleicht ist es Gottes Wille, also muss ich den Kontext von Golang zusammenfassen, in der Hoffnung, einen Bruch mit dem Kontext zu machen.

Wenn wir verschiedene Dienste im Unternehmen schreiben, müssen wir den Kontext als ersten Parameter verwenden. Zuerst dachte ich, dass er hauptsächlich zur Fehlerbehebung und Nachverfolgung von vollständigen Links verwendet wird. Aber mit mehr Kontakt stellt sich heraus, dass es mehr als das ist.

Text

1.Kontext Detaillierte Erklärung

1.1 Generationshintergrund

Vor golang.org/x/net/context war der Kontext noch nicht kompiliert (enthalten in golang.org/x/net/context). wird an vielen Stellen verwendet, daher wurde es in die

1.7-Version integriert und offiziell in die Standardbibliothek aufgenommen

.

Häufige Nutzungspositionen des Kontexts: 1. Bei der Webprogrammierung entspricht eine Anfrage der Dateninteraktion zwischen mehreren Goroutinen. 3. Kontextsteuerung.

1.2 Die zugrunde liegende Struktur des Kontexts die zugrunde liegende Datenstruktur:


Feld

Bedeutung

Deadline gibt eine Zeit zurück. Zeit, die den Zeitpunkt angibt, zu dem der aktuelle Kontext enden soll, ok bedeutet, dass es eine Endzeit gibt FertigEin geschlossener Kanal wird zurückgegeben, wenn der Kontext abgebrochen wird oder eine Zeitüberschreitung auftritt, und weist die kontextbezogenen Funktionen an, die aktuelle Arbeit zu stoppen und zurückzukehren. (Das ist ein bisschen wie eine globale Übertragung) ErrDer Grund, warum der Kontext abgebrochen wurdeValuecontext implementiert einen gemeinsamen Datenspeicher, der Coroutine-sicher ist (denken Sie daran, dass Map zuvor als unsicher galt). ?Wenn Sie also auf die Kartenstruktur stoßen und diese nicht sync.Map ist, müssen Sie sie für den Betrieb sperren)
type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
Gleichzeitig definiert das Paket auch die Schnittstelle, die implementiert werden muss, um den Abbruch bereitzustellen Funktion. Dies liegt hauptsächlich daran, dass das später erwähnte „Abbruchsignal und Timeout-Signal“ implementiert werden muss. Dann bietet die Bibliothek 4 Kontextimplementierungen, mit denen jeder spielen kann völlig leer Kontext, Implementierung Die Funktionen kehren auch zurück Null, sie implementieren nur die Kontextschnittstelle
cancelCtx

type cancelCtx struct {

 Context

 mu  sync.Mutex  done  chan struct{}  children map[c  anceler]struct{} } cancelCtx geerbt, Timeout-Mechanismus hinzugefügt  Context}speichert Schlüssel-Wert-Paardaten
error Deadline Time.Time Von
valueCtx Typ valueCtx struct {  key, val interface{}

1.3 context的创建

为了更方便的创建Context,包里头定义了Background来作为所有Context的根,它是一个emptyCtx的实例。

var (
    background = new(emptyCtx)
    todo       = new(emptyCtx) // 
)

func Background() Context {
    return background
}

你可以认为所有的Context是树的结构,Background是树的根,当任一Context被取消的时候,那么继承它的Context 都将被回收。

2.context实战应用

2.1 WithCancel

实现源码:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
	c := newCancelCtx(parent)
	propagateCancel(parent, &c)
	return &c, func() { c.cancel(true, Canceled) }
}

实战场景:
执行一段代码,控制执行到某个度的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃0-5个,计算吃到10的用时
实战代码:

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	eatNum := chiHanBao(ctx)
	for n := range eatNum {
		if n >= 10 {
			cancel()
			break
		}
	}

	fmt.Println("正在统计结果。。。")
	time.Sleep(1 * time.Second)
}

func chiHanBao(ctx context.Context) <-chan int {
	c := make(chan int)
	// 个数
	n := 0
	// 时间
	t := 0
	go func() {
		for {
			//time.Sleep(time.Second)
			select {
			case <-ctx.Done():
				fmt.Printf("耗时 %d 秒,吃了 %d 个汉堡 \n", t, n)
				return
			case c <- n:
				incr := rand.Intn(5)
				n += incr
				if n >= 10 {
					n = 10
				}
				t++
				fmt.Printf("我吃了 %d 个汉堡\n", n)
			}
		}
	}()

	return c
}

输出:

我吃了 1 个汉堡
我吃了 3 个汉堡
我吃了 5 个汉堡
我吃了 9 个汉堡
我吃了 10 个汉堡
正在统计结果。。。
耗时 6 秒,吃了 10 个汉堡

2.2 WithDeadline & WithTimeout

实现源码:

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
	if cur, ok := parent.Deadline(); ok && cur.Before(d) {
		// The current deadline is already sooner than the new one.
		return WithCancel(parent)
	}
	c := &timerCtx{
		cancelCtx: newCancelCtx(parent),
		deadline:  d,
	}
	propagateCancel(parent, c)
	dur := time.Until(d)
	if dur <= 0 {
		c.cancel(true, DeadlineExceeded) // deadline has already passed
		return c, func() { c.cancel(true, Canceled) }
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.err == nil {
		c.timer = time.AfterFunc(dur, func() {
			c.cancel(true, DeadlineExceeded)
		})
	}
	return c, func() { c.cancel(true, Canceled) }
}

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}

实战场景:
执行一段代码,控制执行到某个时间的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃0-5个,用时10秒,可以吃多少个
实战代码:

func main() {
    // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10))
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	chiHanBao(ctx)
	defer cancel()
}

func chiHanBao(ctx context.Context) {
	n := 0
	for {
		select {
		case <-ctx.Done():
			fmt.Println("stop \n")
			return
		default:
			incr := rand.Intn(5)
			n += incr
			fmt.Printf("我吃了 %d 个汉堡\n", n)
		}
		time.Sleep(time.Second)
	}
}

输出:

我吃了 1 个汉堡
我吃了 3 个汉堡
我吃了 5 个汉堡
我吃了 9 个汉堡
我吃了 10 个汉堡
我吃了 13 个汉堡
我吃了 13 个汉堡
我吃了 13 个汉堡
我吃了 14 个汉堡
我吃了 14 个汉堡
stop

2.3 WithValue

实现源码:

func WithValue(parent Context, key, val interface{}) Context {
	if key == nil {
		panic("nil key")
	}
	if !reflect.TypeOf(key).Comparable() {
		panic("key is not comparable")
	}
	return &valueCtx{parent, key, val}
}

实战场景:
携带关键信息,为全链路提供线索,比如接入elk等系统,需要来一个trace_id,那WithValue就非常适合做这个事。
实战代码:

func main() {
	ctx := context.WithValue(context.Background(), "trace_id", "88888888")
	// 携带session到后面的程序中去
	ctx = context.WithValue(ctx, "session", 1)

	process(ctx)
}

func process(ctx context.Context) {
	session, ok := ctx.Value("session").(int)
	fmt.Println(ok)
	if !ok {
		fmt.Println("something wrong")
		return
	}

	if session != 1 {
		fmt.Println("session 未通过")
		return
	}

	traceID := ctx.Value("trace_id").(string)
	fmt.Println("traceID:", traceID, "-session:", session)
}

输出:

traceID: 88888888 -session: 1

3.context建议

不多就一个。

Context要是全链路函数的第一个参数

func myTest(ctx context.Context)  {
    ...
}

(写好了竟然忘记发送了。。。汗)

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Golang-Kontexts. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen