ホームページ  >  記事  >  バックエンド開発  >  Golangコンテキストの詳しい説明

Golangコンテキストの詳しい説明

藏色散人
藏色散人転載
2020-09-10 09:30:294010ブラウズ

次のコラム golang チュートリアル では、Golang のコンテキストについて詳しく説明します。困っている友人の役に立てば幸いです。

Golangコンテキストの詳しい説明

#序文

はい、今日はもともと欲しかったものです外に出て遊ぶこと。電車の切符を買ったのに、また寝坊してしまいました。 。仕方がありません、おそらく神のご意志なので、文脈との決別を望んで、golang の文脈を要約する必要があります。

社内でさまざまなサービスを記述する際、最初のパラメータとして Context を使用する必要があるのですが、最初は主にフルリンクのトラブルシューティングと追跡に使用されると考えていました。しかし、接触が増えると、それ以上のものであることがわかります。

Text

1.コンテキストの詳細説明

1.1 生成背景

go 1.7 より前は、コンテキストはまだプログラムされていませんでした (golang.org/ を含む) x /net/context) を使用すると、golang チームは context が非常に使いやすく、多くの場所で使用されることに気づき、それを標準ライブラリ

バージョン 1.7 に組み込みました。 コンテキストの一般的な使用姿勢:

1. Web プログラミングでは、1 つのリクエストが複数のゴルーチン間のデータ対話に対応します

2. タイムアウト制御
3. コンテキスト制御

1.2 コンテキストの基礎となる構造

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

これはコンテキストの基礎となるデータ構造です。分析しましょう:

フィールド#Deadline現在のコンテキストが終了する時刻を示す time.Time を返します。OK は終了時刻があることを意味しますDoneコンテキストがキャンセルまたはタイムアウトしたときにクローズ チャネルが返され、コンテキスト関連関数に現在の作業を停止して戻るように指示します。 (これはグローバル ブロードキャストに少し似ています)Errコンテキストがキャンセルされた理由Value コンテキストが共有データ ストレージを実装する場所はコルーチンに対して安全です (マップは安全ではないという前に述べたことを覚えていますか? したがって、マップ構造に遭遇したときに、それが sync.Map でない場合は、操作するためにロックする必要があります) 同時に、パッケージはキャンセル機能を提供するために実装する必要があるインターフェイスも定義します。これは主に、後述する「キャンセル信号とタイムアウト信号」を実装する必要があるためです。
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
	cancel(removeFromParent bool, err error)
	Done() <-chan struct{}
}
意味
その後、ライブラリは誰でも遊べる 4 つのコンテキスト実装を提供します

実装構造関数#emptyCtxtype emptyCtx inttype cancelCtx struct { Context 完了 chan struct{}type timerCtx struct { cancelCtx 期限下 time.Timetype valueCtx struct { Context key、val インターフェイス{}

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

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

完全に空の Context。実装された関数も nil を返します。Context インターフェイスを実装しているだけです。 cancelCtx
mu sync.Mutex
子マップ[canceler]struct{}
err error
}

Context から継承し、キャンセラー インターフェイスも実装します

timerCtx
timer *time.Timer // cancelCtx.mu.}



cancelCtx## から継承#、タイムアウト メカニズムの追加
valueCtx
} キーと値のペアを格納するデータ


以上がGolangコンテキストの詳しい説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。