...

パッケージ context

import "context"
概要
目次

概要 ▾

context パッケージは Context 型を定義します。 デッドライン,キャンセルシグナルや他の API 間,プロセス間のリクエストに関する値を処理します。

サーバーへの受信リクエストは Context を作成し,サーバーへの発信呼び出しは Context を受け入れる必要があります。 それらの間の一連の関数呼び出しは,Context を伝播する必要があり,オプションで WithCancel, WithDeadline, WithTimeout, または WithValue を使用して作成された派生 Context と置き換えます。 Context がキャンセルされると,その Context から派生したすべての Context もキャンセルされます。

WithCancel, WithDeadline, および WithTimeout 関数は, Context (親) を受け取り,派生 Context (子) と CancelFunc を返します。 CancelFunc を呼び出すと,子とその子をキャンセルし,親から子への参照を削除して,関連付けられているタイマーをすべて停止します。 CancelFunc を呼び出さないと,親がキャンセルされるかタイマーが起動するまで,子とその子にリークが発生します。 go vet ツールは, CancelFunc がすべての制御フローパスで使用されていることを確認します。

Context を使用するプログラムは,パッケージ間でインターフェースの一貫性を保ち,静的解析ツールがコンテキストの伝播をチェックできるようにするために,以下の規則に従う必要があります。

Context を構造体型の中に格納しないでください。 代わりに, Context を必要とする各関数に明示的に渡してください。 Context は最初のパラメータで,通常は ctx という名前です。

func DoSomething(ctx context.Context, arg Arg) error {
	// ... use ctx ...
}

たとえ関数が許すとしても, nil Context を渡さないでください。 どの Context を使用すべきかわからない場合は, context.TODO を渡します。

コンテキスト Value は,プロセスや API を通過するリクエストスコープのデータにのみ使用し,オプションのパラメータを関数に渡すために使用しないでください。

同じ Context を異なるゴルーチンで実行されている関数に渡すことができます。 Context は複数のゴルーチンによる同時使用に対して安全です。

Context を使用するサーバーのコード例については, https://blog.golang.org/context を参照してください。

変数

Canceled は,コンテキストがキャンセルされたときに Context.Err によって返されるエラーです。

var Canceled = errors.New("context canceled")

DeadlineExceeded は,コンテキストの期限が過ぎたときに Context.Err によって返されるエラーです。

var DeadlineExceeded error = deadlineExceededError{}

func WithCancel 1.7

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel は新しい Done チャンネルを持つ parent のコピーを返します。 返されたコンテキストの Done チャンネルは, 返された cancel 関数が呼び出されたとき, または親コンテキストの Done チャンネルが閉じられたときのどちらか早いほうで閉じられます。

このコンテキストをキャンセルするとそれに関連するリソースが解放されるので, このコンテキストで実行されている操作が完了するとすぐにコードは cancel を呼び出すべきです。

この例では,ゴルーチンのリークを防ぐためのキャンセル可能なコンテキストの使用方法を示します。 例の関数の終わりまでに, gen によって開始されたゴルーチンはリークせずに戻ります。

コード:

// gen は別のゴルーチンで整数を生成し,
// それらを返り値のチャンネルに送信します。
// gen の呼び出し元は, gen によって開始された内部ゴルーチンが
// リークしないように,生成された整数を消費し終わったら,
// コンテキストをキャンセルする必要があります。
gen := func(ctx context.Context) <-chan int {
    dst := make(chan int)
    n := 1
    go func() {
        for {
            select {
            case <-ctx.Done():
                return // ゴルーチンをリークさせないために戻る
            case dst <- n:
                n++
            }
        }
    }()
    return dst
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 整数を使い終わったらキャンセルする

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

出力:

1
2
3
4
5

func WithDeadline 1.7

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline は,期限が d より遅くならないように調整された親コンテキストのコピーを返します。 親の期限がすでに d より早い場合, WithDeadline(parent, d) は意味的に parent と同等です。 返却されたコンテキストの Done チャンネルは, 期限が切れる,返却された cancel 関数が呼ばれた, または親コンテキストの Done チャンネルが閉じられたのどれか早い方で閉じられます。

このコンテキストをキャンセルするとそれに関連するリソースが解放されるので, このコンテキストで実行されている操作が完了するとすぐにコードは cancel を呼び出すべきです。

この例では,任意の期限を設定したコンテキストを渡して,期限に到達したらすぐに中止するようブロックしている関数に指示します。

コード:

d := time.Now().Add(50 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)

// ctx はいずれ期限切れになりますが,いずれにしてもキャンセル関数を呼び出すことをお勧めします。
// そうしないと,コンテキストとその親が必要以上に長く生き続けることがあります。
defer cancel()

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

出力:

context deadline exceeded

func WithTimeout 1.7

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout は WithDeadline(parent, time.Now().Add(timeout)) を返します。

このコンテキストをキャンセルするとそれに関連するリソースが解放されるので, このコンテキストで実行されている操作が完了するとすぐにコードは cancel を呼び出すべきです。

func slowOperationWithTimeout(ctx context.Context) (Result, error) {
	ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
	defer cancel()  // タイムアウトが経過する前に slowOperation が完了するとリソースを解放します
	return slowOperation(ctx)
}

この例では,タイムアウト付きのコンテキストを渡して,タイムアウトが経過した後に,作業を放棄する必要があることをブロックしている関数に伝えます。

コード:

// タイムアウト付きのコンテキストを渡して,タイムアウトが経過した後に作業を放棄するようブロックしている関数に伝えます。
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()) // "context deadline exceeded" と表示します
}

出力:

context deadline exceeded

type CancelFunc 1.7

CancelFunc は,作業を中止するよう指示します。 CancelFunc は,作業が停止するのを待ちません。 CancelFunc は,複数のゴルーチンから平行に呼び出すことができます。 最初の呼び出しの後, CancelFunc への後続の呼び出しは何もしません。

type CancelFunc func()

type Context 1.7

Context は, API の境界を越えて期限,キャンセルシグナル,その他の値を伝えます。

Context のメソッドは,複数のゴルーチンによって同時に呼び出されることがあります。

type Context interface {
    // Deadline は,このコンテキストで行われる作業をキャンセルする必要がある時間を返します。
    // 期限が設定されていない場合, Deadline は ok==false を返します。
    // Deadline を続けて呼び出しても同じ結果が返されます。
    Deadline() (deadline time.Time, ok bool)

    // Done は,このコンテキストで行われる作業をキャンセルする必要があるときに閉じられるチャンネルを返します。
    // このコンテキストがキャンセルされることが決してないならば, Done は nil を返すかもしれません。
    // Done を続けて呼び出しても同じ値が返されます。
    //
    // WithCancel は, キャンセルが呼び出されたときに Done が閉じられるようにします。
    // WithDeadline は,期限が切れると Done が閉じられるようにします。
    // WithTimeout は,タイムアウトが経過したときに Done が閉じられるようにします。
    //
    // Done は,select ステートメントで使用するために用意されています。
    //
    //  // Stream は DoSomething で値を生成し,その値を
    //  // DoSomething がエラーを返すか ctx.Done がクローズされるまで out に送信します。
    //  func Stream(ctx context.Context, out chan<- Value) error {
    //  	for {
    //  		v, err := DoSomething(ctx)
    //  		if err != nil {
    //  			return err
    //  		}
    //  		select {
    //  		case <-ctx.Done():
    //  			return ctx.Err()
    //  		case out <- v:
    //  		}
    //  	}
    //  }
    //
    // キャンセルに Done チャンネルを使用する方法の例については,
    // https://blog.golang.org/pipelines を参照してください。
    Done() <-chan struct{}

    // Done がまだ閉じられていない場合, Err は nil を返します。
    // Done が閉じられた場合, Err は理由を説明する以下の非 nil エラーを返します。
    // Canceled は,コンテキストがキャンセルされた場合。
    // DeadlineExceeded はコンテキストの期限が過ぎた場合。
    // Err が nil 以外のエラーを返した後, Err を続けて呼び出すと同じエラーが返されます。
    Err() error

    // Value はこのコンテキストで key に関連付けられた値を返します。
    // key に関連付けられた値がない場合は nil を返します。
    // 同じ key を使用して Value を連続して呼び出すと,同じ結果が返されます。
    //
    // コンテキスト値は,プロセスや API の境界を通過するリクエストスコープのデータに対してのみ使用し,
    // オプションのパラメータを関数に渡すためには使用しないでください。
    //
    // key は,Context 内の特定の値を識別します。
    // Context に値を格納したい関数は,通常,グローバル変数にキーを割り当て,
    // そのキーを Context.WithValue および Context.Value への引数として使用します。
    // キーは,等価性をサポートする任意の型にすることができます。
    // 衝突を避けるために,パッケージはキーをエクスポートされていない型として定義する必要があります。
    //
    // Context キーを定義するパッケージは, そのキーで格納されている値に対して
    // 型保証されたアクセサを提供する必要があります。
    //
    // 	// user パッケージは,Contexts に格納される User 型を定義します。
    // 	package user
    //
    // 	import "context"
    //
    // 	// User は Contexts に格納される値の型です。
    // 	type User struct {...}
    //
    // 	// key は,このパッケージで定義されているキーのエクスポートされていない型です。
    // 	// これは他のパッケージで定義されているキーとの衝突を防ぎます。
    // 	type key int
    //
    // 	// userKey は, Context 内の user.User 値のキーです。 エクスポートされていません。
    // 	// クライアントは,このキーを直接使用する代わりに,
    // 	// user.NewContext および user.FromContext を使用します。
    // 	var userKey key
    //
    // 	// NewContext は,値 u を持つ新しい Context を返します。
    // 	func NewContext(ctx context.Context, u *User) context.Context {
    // 		return context.WithValue(ctx, userKey, u)
    // 	}
    //
    // 	// FromContext は, ctx に格納されている User 値があればそれを返します。
    // 	func FromContext(ctx context.Context) (*User, bool) {
    // 		u, ok := ctx.Value(userKey).(*User)
    // 		return u, ok
    // 	}
    Value(key interface{}) interface{}
}

func Background 1.7

func Background() Context

Background は nil でない空の Context を返します。 キャンセルされることはなく,値を持つこともなく,期限もありません。 通常, main 関数,初期化,テストによって使用され,受信リクエストの最上位コンテキストとして使用されます。

func TODO 1.7

func TODO() Context

TODO は nil ではない空の Context を返します。 どの Context を使用すべきか不明な場合, または(周囲の関数が Context パラメータを受け入れるようにまだ拡張されていないため)まだ使用できない場合は, コードで context.TODO を使用する必要があります。

func WithValue 1.7

func WithValue(parent Context, key, val interface{}) Context

WithValue は, key に関連付けられた値が val である parent のコピーを返します。

コンテキスト Value は,プロセスや API を通過するリクエストスコープのデータにのみ使用し,オプションのパラメータを関数に渡すために使用しないでください。

key は比較可能でなければならず, コンテキストを使用したパッケージ間の衝突を避けるために, 文字列型または他の組み込み型であってはなりません。 WithValue を使う側は,key に独自の型を定義する必要があります。 interface{} への代入時に割り当てを避けるために, コンテキストキーはしばしば具象型 struct{} です。 あるいは,エクスポートされたコンテキストキー変数の静的型は ポインタまたはインターフェースであるべきです。

この例では,値をコンテキストに渡す方法と,値が存在する場合にそれを取得する方法を示します。

コード:

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

出力:

found value: Go
key not found: color