Documentation
¶
Overview ¶
contextパッケージは、期限、キャンセルシグナル、および他のAPI境界やプロセス間を超えたリクエストスコープの値を伝達するContext型を定義します。
サーバーへの受信リクエストは Context を作成し、サーバーへの送信 呼び出しはContextを受け入れる必要があります。それらの間の関数 呼び出しのチェーンはContextを伝播する必要があり、オプションで WithCancel、WithDeadline、WithTimeout、または WithValue を使用して 作成された派生Contextで置き換えることができます。
Contextは、その代わりに実行される作業を停止すべきことを示すためにキャンセルされる場合があります。 期限のあるContextは、期限が過ぎた後にキャンセルされます。 Contextがキャンセルされると、そこから派生したすべてのContextもキャンセルされます。
WithCancel、WithDeadline、および WithTimeout 関数は Context(親)を受け取り、派生Context(子)と CancelFunc を返します。CancelFuncを直接呼び出すと、子とその 子たちがキャンセルされ、親の子への参照が削除され、 関連するタイマーが停止されます。CancelFuncの呼び出しに失敗すると、 親がキャンセルされるまで子とその子たちがリークします。go vetツールは CancelFuncがすべての制御フローパスで使用されているかをチェックします。
WithCancelCause、WithDeadlineCause、および WithTimeoutCause 関数は CancelCauseFunc を返します。これはエラーを受け取り、それを キャンセルの原因として記録します。キャンセルされたコンテキスト またはその子のいずれかで Cause を呼び出すと、原因が取得されます。原因が指定されていない場合、 Cause(ctx)はctx.Err()と同じ値を返します。
Contextを使用するプログラムは、これらのルールに従う必要があります。 これにより、パッケージ間でインターフェースを一貫させ、静的解析ツールがコンテキストの伝播をチェックできるようになります。
Contextを構造体型の内部に格納しないでください。代わりに、必要とする 各関数にContextを明示的に渡してください。これについては https://go.dev/blog/context-and-structs でさらに詳しく説明されています。Contextは最初の パラメータであるべきで、通常はctxという名前を付けます:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
関数がnilの Context を許可していても、nilの Context を渡さないでください。 どの Context を使用するかわからない場合は、 context.TODO を渡してください。
コンテキストの値は、オプションのパラメータを関数に渡すためではなく、プロセスやAPIを超えるリクエストスコープのデータにのみ使用してください。
同じContextは、異なるゴルーチンで実行される関数に渡すことができます。Contextは、複数のゴルーチンによる同時使用に対して安全です。
Contextを使用するサーバーのサンプルコードについては https://go.dev/blog/context を参照してください。
Index ¶
- Variables
- func AfterFunc(ctx Context, f func()) (stop func() bool)
- func Cause(c Context) error
- func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
- func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
- func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
- func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
- func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
- func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
- type CancelCauseFunc
- type CancelFunc
- type Context
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Canceled = errors.New("context canceled")
Canceledは、期限の経過以外の理由でコンテキストがキャンセルされた場合に [Context.Err] によって返されるエラーです。
var DeadlineExceeded error = deadlineExceededError{}
DeadlineExceededは、コンテキストの期限が切れた場合に [Context.Err] によって返されるエラーです。
Functions ¶
func AfterFunc ¶ added in v1.21.0
AfterFuncは、ctxがキャンセルされた後に独自のゴルーチンでfを呼び出すように手配します。 ctxが既にキャンセルされている場合、AfterFuncは独自のゴルーチンで即座にfを呼び出します。
ContextでのAfterFuncの複数回の呼び出しは独立して動作し、1つが他を置き換えることはありません。
返されたstop関数を呼び出すと、ctxとfの関連付けが停止されます。 fの実行が停止された場合、trueを返します。 stopがfalseを返す場合、 コンテキストがキャンセルされてfが独自のゴルーチンで開始されているか、 またはfが既に停止されています。 stop関数は、fの完了を待たずに戻ります。 呼び出し元がfが完了したかどうかを知る必要がある場合、 fと明示的に協調する必要があります。
ctxに「AfterFunc(func()) func() bool」メソッドがある場合、 AfterFuncはそれを使用して呼び出しをスケジュールします。
Example (Cond) ¶
This example uses AfterFunc to define a function which waits on a sync.Cond, stopping the wait when a context is canceled.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/sync"
"github.com/shogo82148/std/time"
)
func main() {
waitOnCond := func(ctx context.Context, cond *sync.Cond, conditionMet func() bool) error {
stopf := context.AfterFunc(ctx, func() {
// We need to acquire cond.L here to be sure that the Broadcast
// below won't occur before the call to Wait, which would result
// in a missed signal (and deadlock).
cond.L.Lock()
defer cond.L.Unlock()
// If multiple goroutines are waiting on cond simultaneously,
// we need to make sure we wake up exactly this one.
// That means that we need to Broadcast to all of the goroutines,
// which will wake them all up.
//
// If there are N concurrent calls to waitOnCond, each of the goroutines
// will spuriously wake up O(N) other goroutines that aren't ready yet,
// so this will cause the overall CPU cost to be O(N²).
cond.Broadcast()
})
defer stopf()
// Since the wakeups are using Broadcast instead of Signal, this call to
// Wait may unblock due to some other goroutine's context being canceled,
// so to be sure that ctx is actually canceled we need to check it in a loop.
for !conditionMet() {
cond.Wait()
if ctx.Err() != nil {
return ctx.Err()
}
}
return nil
}
cond := sync.NewCond(new(sync.Mutex))
var wg sync.WaitGroup
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
cond.L.Lock()
defer cond.L.Unlock()
err := waitOnCond(ctx, cond, func() bool { return false })
fmt.Println(err)
}()
}
wg.Wait()
}
Output: context deadline exceeded context deadline exceeded context deadline exceeded context deadline exceeded
Example (Connection) ¶
This example uses AfterFunc to define a function which reads from a net.Conn, stopping the read when a context is canceled.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/net"
"github.com/shogo82148/std/time"
)
func main() {
readFromConn := func(ctx context.Context, conn net.Conn, b []byte) (n int, err error) {
stopc := make(chan struct{})
stop := context.AfterFunc(ctx, func() {
conn.SetReadDeadline(time.Now())
close(stopc)
})
n, err = conn.Read(b)
if !stop() {
// AfterFuncが開始されました。
// 完了するまで待ち、Connの期限をリセットします。
<-stopc
conn.SetReadDeadline(time.Time{})
return n, ctx.Err()
}
return n, err
}
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
fmt.Println(err)
return
}
defer listener.Close()
conn, err := net.Dial(listener.Addr().Network(), listener.Addr().String())
if err != nil {
fmt.Println(err)
return
}
defer conn.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
b := make([]byte, 1024)
_, err = readFromConn(ctx, conn, b)
fmt.Println(err)
}
Output: context deadline exceeded
Example (Merge) ¶
This example uses AfterFunc to define a function which combines the cancellation signals of two Contexts.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/errors"
"github.com/shogo82148/std/fmt"
)
func main() {
// mergeCancelは、ctxの値を含み、ctxまたはcancelCtxのいずれかがキャンセルされたときにキャンセルされるコンテキストを返します。
mergeCancel := func(ctx, cancelCtx context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancelCause(ctx)
stop := context.AfterFunc(cancelCtx, func() {
cancel(context.Cause(cancelCtx))
})
return ctx, func() {
stop()
cancel(context.Canceled)
}
}
ctx1, cancel1 := context.WithCancelCause(context.Background())
defer cancel1(errors.New("ctx1 canceled"))
ctx2, cancel2 := context.WithCancelCause(context.Background())
mergedCtx, mergedCancel := mergeCancel(ctx1, ctx2)
defer mergedCancel()
cancel2(errors.New("ctx2 canceled"))
<-mergedCtx.Done()
fmt.Println(context.Cause(mergedCtx))
}
Output: ctx2 canceled
func Cause ¶ added in v1.20.0
Causeは、cがキャンセルされた理由を説明する非nilのエラーを返します。 cまたはその親の最初のキャンセルは原因を設定します。 そのキャンセルがCancelCauseFunc(err)の呼び出しによって行われた場合、 Cause はerrを返します。 そうでない場合、Cause(c)はc.Err()と同じ値を返します。 cがまだキャンセルされていない場合、Causeはnilを返します。
func WithCancel ¶
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancelは親のコンテキストを指す派生コンテキストを返しますが、 新しいDoneチャネルを持ちます。返されたコンテキストのDoneチャネルは、 返されたキャンセル関数が呼び出されたとき、または親のコンテキストの Doneチャネルが閉じられたときのいずれか最初に発生したときに閉じられます。
このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、コードはこの Context で実行される操作が完了したらすぐにcancelを呼び出す必要があります。
Example ¶
This example demonstrates the use of a cancelable context to prevent a goroutine leak. By the end of the example function, the goroutine started by gen will return without leaking.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/fmt"
)
func main() {
// genは、別のゴルーチンで整数を生成し、それらを返されたチャネルに送信します。
// genの呼び出し元は、生成された整数を消費し終わったらコンテキストをキャンセルする必要があります。
// そうしないと、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() // 整数を消費し終わったらキャンセル
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
}
Output: 1 2 3 4 5
func WithCancelCause ¶ added in v1.20.0
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
WithCancelCause WithCancel と同様に動作しますが、 CancelFunc の代わりに CancelCauseFunc を返します。 エラー(「原因」と呼ばれる)を非nilで渡すと、そのエラーがctxに記録されます。 その後、Cause を使用して取得できます。 nilでキャンセルすると、原因は Canceled に設定されます。
使用例:
ctx、cancel := context.WithCancelCause(parent) cancel(myError) ctx.Err() // context.Canceledを返します context.Cause(ctx) // myErrorを返します
func WithDeadline ¶
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
WithDeadlineは親のコンテキストを指す派生コンテキストを返しますが、 期限はd以降にならないように調整されます。親の 期限が既にdより早い場合、WithDeadline(parent, d)は意味的に 親と同等です。返された[Context.Done]チャネルは、 期限が切れたとき、返されたキャンセル関数が呼び出されたとき、 または親のコンテキストのDoneチャネルが閉じられたときのいずれか最初に発生したときに閉じられます。
このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、コードはこの Context で実行される操作が完了したらすぐにcancelを呼び出す必要があります。
Example ¶
This example passes a context with an arbitrary deadline to tell a blocking function that it should abandon its work as soon as it gets to it.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/time"
)
var shortDuration time.Duration = 1
var neverReady <-chan time.Time
func main() {
d := time.Now().Add(shortDuration)
ctx, cancel := context.WithDeadline(context.Background(), d)
// ctxが期限切れになっている場合でも、そのキャンセル関数を呼び出すことは良い習慣です。
// そうしないと、コンテキストとその親が必要以上に長く生き残る可能性があります。
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
Output: context deadline exceeded
func WithDeadlineCause ¶ added in v1.21.0
WithDeadlineCause WithDeadline と同様に動作しますが、期限が切れたときに返された Context の原因も設定します。 返された CancelFunc は原因を設定しません。
func WithTimeout ¶
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeoutはWithDeadline(parent, time.Now().Add(timeout))を返します。
このコンテキストをキャンセルすると、それに関連するリソースが解放されるため、 コードはこの Context で実行される操作が完了したらすぐにcancelを呼び出す必要があります。
func slowOperationWithTimeout(ctx context.Context) (Result, error) {
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel() // slowOperationがタイムアウトが経過する前に完了した場合、リソースが解放されます
return slowOperation(ctx)
}
Example ¶
This example passes a context with a timeout to tell a blocking function that it should abandon its work after the timeout elapses.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/fmt"
"github.com/shogo82148/std/time"
)
var shortDuration time.Duration = 1
var neverReady <-chan time.Time
func main() {
// タイムアウト付きのコンテキストを渡すことで、ブロッキング関数に、タイムアウトが経過した後に作業を中止するように指示します。
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err()) // "context deadline exceeded" を出力します
}
}
Output: context deadline exceeded
func WithTimeoutCause ¶ added in v1.21.0
WithTimeoutCause [WithTimeout]と同様に動作しますが、タイムアウトが切れたときに返された Context の原因も設定します。 返された CancelFunc は原因を設定しません。
Types ¶
type CancelCauseFunc ¶ added in v1.20.0
type CancelCauseFunc func(cause error)
CancelCauseFunc [CancelFunc]と同様に動作しますが、キャンセルの原因を設定します。 この原因は、キャンセルされたContextまたはその派生Contextのいずれかで Cause を呼び出すことで取得できます。
コンテキストが既にキャンセルされている場合、 CancelCauseFunc は原因を設定しません。 たとえば、childContextがparentContextから派生している場合:
- childContextがcause2でキャンセルされる前に、parentContextがcause1でキャンセルされた場合、 その後、Cause(parentContext) == Cause(childContext) == cause1
- parentContextがcause1でキャンセルされる前に、childContextがcause2でキャンセルされた場合、 その後、Cause(parentContext) == cause1 および Cause(childContext) == cause2
type CancelFunc ¶
type CancelFunc func()
CancelFunc 操作がその作業を中止するように指示します。 CancelFuncは、作業が停止するのを待ちません。 CancelFuncは、複数のゴルーチンから同時に呼び出すことができます。 最初の呼び出しの後、CancelFuncへの後続の呼び出しは何もしません。
type Context ¶
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
Context は、期限、キャンセルシグナル、および他の値をAPI境界を超えて伝達します。
Contextのメソッドは、複数のゴルーチンから同時に呼び出すことができます。
func Background ¶
func Background() Context
Backgroundは、非nilで空の Context を返します。キャンセルされることはなく、値も期限もありません。 通常、main関数、初期化、テスト、および着信リクエストのトップレベルContextとして使用されます。
func TODO ¶
func TODO() Context
TODOは、非nilで空の Context を返します。 コードがどの Context を使用するか不明である場合や、まだ Context パラメータを受け入れるように拡張されていない (周囲の関数がまだ Context を受け入れるように拡張されていない)場合に、コードは context.TODO を使用する必要があります。
func WithValue ¶
WithValueは親のContextを指す派生コンテキストを返します。 派生コンテキストでは、keyに関連付けられた値はvalです。
コンテキストの値は、プロセスやAPIを超えて転送されるリクエストスコープのデータにのみ使用し、関数にオプションのパラメータを渡すために使用しないでください。
提供されたキーは比較可能である必要があり、衝突を避けるためにstringまたは他の組み込み型であってはなりません。 WithValueを使用するユーザーは、キーのために独自の型を定義する必要があります。 interface{} に代入するときのアロケーションを避けるために、コンテキストキーは通常、具体的な型 struct{} を持ちます。 代替案として、エクスポートされたコンテキストキー変数の静的型はポインタまたはインターフェースである必要があります。
Example ¶
This example demonstrates how a value can be passed to the context and also how to retrieve it if it exists.
package main
import (
"github.com/shogo82148/std/context"
"github.com/shogo82148/std/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"))
}
Output: found value: Go key not found: color
func WithoutCancel ¶ added in v1.21.0
WithoutCancelは親のコンテキストを指す派生コンテキストを返し、 親がキャンセルされたときにキャンセルされません。 返されたコンテキストはDeadlineやErrを返さず、そのDoneチャネルはnilです。 返されたコンテキストで[Cause]を呼び出すとnilを返します。