Пакет context
Обзор
Пакет context определяет тип Context, который передаёт сроки выполнения, сигналы отмены и другие значения, относящиеся к запросу, через границы API и между процессами.
Входящие запросы к серверу должны создавать Context, а исходящие вызовы серверов должны принимать Context. Цепочка вызовов функций между ними должна передавать Context, при необходимости заменяя его производным Context, созданным с помощью WithCancel, WithDeadline, WithTimeout или WithValue.
Context можно отменить, чтобы указать, что работа, выполняемая от его имени, должна быть остановлена. Context со сроком выполнения отменяется по истечении этого срока. При отмене Context отменяются также все производные от него Context.
Функции WithCancel, WithDeadline и WithTimeout принимают Context (родительский) и возвращают производный Context (дочерний) и CancelFunc. Прямой вызов CancelFunc отменяет дочерний Context и его потомков, удаляет ссылку родителя на дочерний Context и останавливает связанные таймеры. Если не вызвать CancelFunc, дочерний Context и его потомки будут оставаться в памяти до отмены родительского Context. Инструмент go vet проверяет, что CancelFunc вызываются во всех ветвях потока управления.
Функция WithCancelCause возвращает CancelCauseFunc, которая принимает ошибку и записывает её как причину отмены. Функции WithDeadlineCause и WithTimeoutCause принимают причину, которая будет использоваться при истечении срока выполнения. Вызов Cause для отменённого Context или любого его потомка возвращает причину отмены. Если причина не указана, 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, даже если функция это допускает. Если вы не уверены, какой Context использовать, передайте context.TODO.
Используйте значения Context только для данных, относящихся к запросу и передаваемых между процессами и API, а не для передачи необязательных параметров функциям.
Один и тот же Context можно передавать функциям, выполняющимся в разных горутинах; Context безопасно использовать одновременно из нескольких горутин.
Пример кода сервера, использующего Context, см. на странице https://go.dev/blog/context.
Индекс
Примеры
Файлы пакета
context.go
Переменные
Canceled — ошибка, возвращаемая методом Context.Err, когда контекст отменён по причине, отличной от истечения срока его действия.
var Canceled = errors.New("context canceled") DeadlineExceeded — ошибка, возвращаемая методом Context.Err, когда контекст отменён из-за истечения срока его действия.
var DeadlineExceeded error = deadlineExceededError{} func AfterFunc 1.21
func AfterFunc(ctx Context, f func()) (stop func() bool)
AfterFunc организует вызов f в отдельной горутине после отмены ctx. Если ctx уже отменён, AfterFunc немедленно вызывает f в отдельной горутине.
Несколько вызовов AfterFunc для одного контекста работают независимо друг от друга; один вызов не заменяет другой.
Вызов возвращённой функции stop прекращает связь ctx с f. Она возвращает true, если вызов остановил запуск f. Если stop возвращает false, это означает, что либо контекст отменён и f уже запущена в отдельной горутине, либо f уже была остановлена. Функция stop не дожидается завершения f перед возвратом. Если вызывающей стороне необходимо знать, завершилась ли f, она должна явно согласовать это с f.
Если у ctx есть метод "AfterFunc(func()) func() bool", AfterFunc использует его для планирования вызова.
Пример (Cond)
В этом примере AfterFunc используется для определения функции, которая ожидает на sync.Cond и прекращает ожидание при отмене контекста.
Код:
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 range 4 {
wg.Go(func() {
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()
Результат:
context deadline exceeded context deadline exceeded context deadline exceeded context deadline exceeded
Пример (Connection)
В этом примере AfterFunc используется для определения функции, которая читает данные из net.Conn и прекращает чтение при отмене контекста.
Код:
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() {
// The AfterFunc was started.
// Wait for it to complete, and reset the Conn's deadline.
<-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)
Результат:
context deadline exceeded
Пример (Merge)
В этом примере AfterFunc используется для определения функции, объединяющей сигналы отмены двух Context.
Код:
// mergeCancel returns a context that contains the values of ctx,
// and which is canceled when either ctx or cancelCtx is canceled.
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))
Результат:
ctx2 canceled
func Cause 1.20
func Cause(c Context) error
Cause возвращает ненулевую ошибку, объясняющую, почему c был отменен. Причину задает первая отмена c или одного из его родительских контекстов. Если отмена произошла при вызове CancelCauseFunc(err), то Cause возвращает err. В противном случае Cause(c) возвращает то же значение, что и c.Err(). Cause возвращает nil, если c еще не был отменен.
func WithCancel 1.7
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel возвращает производный контекст, который ссылается на родительский контекст, но имеет новый канал Done. Канал Done возвращенного контекста закрывается при вызове возвращенной функции отмены или при закрытии канала Done родительского контекста — в зависимости от того, что произойдет раньше.
Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать cancel сразу после завершения операций, выполняющихся в этом Context.
Пример
В этом примере показано, как использовать отменяемый контекст, чтобы предотвратить утечку горутины. К концу функции-примера горутина, запущенная функцией gen, завершится без утечки.
Код:
// gen generates integers in a separate goroutine and
// sends them to the returned channel.
// The callers of gen need to cancel the context once
// they are done consuming generated integers not to leak
// the internal goroutine started by 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() // cancel when we are finished consuming integers
for n := range gen(ctx) {
fmt.Println(n)
if n == 5 {
break
}
}
Вывод:
1 2 3 4 5
func WithCancelCause 1.20
func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
WithCancel работает как WithCancel, но возвращает CancelCauseFunc вместо CancelFunc. Вызов cancel с ненулевой ошибкой («причиной») сохраняет эту ошибку в ctx; затем ее можно получить с помощью Cause(ctx). Вызов cancel с nil задает в качестве причины Canceled.
Пример использования:
ctx, cancel := context.WithCancelCause(parent) cancel(myError) ctx.Err() // returns context.Canceled context.Cause(ctx) // returns myError
func WithDeadline 1.7
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
WithDeadline возвращает производный контекст, который ссылается на родительский контекст, но имеет крайний срок не позднее d. Если крайний срок родительского контекста наступает раньше d, то WithDeadline(parent, d) семантически эквивалентен parent. Канал Context.Done возвращенного контекста закрывается по истечении крайнего срока, при вызове возвращенной функции отмены или при закрытии канала Done родительского контекста — в зависимости от того, что произойдет раньше.
Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать cancel сразу после завершения операций, выполняющихся в этом Context.
Пример
В этом примере функции, которая может блокироваться, передается контекст с произвольным крайним сроком, чтобы сообщить ей, что нужно прекратить работу, как только она до него дойдет.
Код:
d := time.Now().Add(shortDuration)
ctx, cancel := context.WithDeadline(context.Background(), d)
// Even though ctx will be expired, it is good practice to call its
// cancellation function in any case. Failure to do so may keep the
// context and its parent alive longer than necessary.
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
Вывод:
context deadline exceeded
func WithDeadlineCause 1.21
func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
WithDeadlineCause работает как WithDeadline, но также задает причину возвращенного Context при наступлении крайнего срока. Возвращенный CancelFunc не задает причину.
func WithTimeout 1.7
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout возвращает WithDeadline(parent, time.Now().Add(timeout)).
Отмена этого контекста освобождает связанные с ним ресурсы, поэтому код должен вызывать cancel сразу после завершения операций, выполняющихся в этом Context:
func slowOperationWithTimeout(ctx context.Context) (Result, error) {
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel() // releases resources if slowOperation completes before timeout elapses
return slowOperation(ctx)
}
Пример
В этом примере функции, которая может блокироваться, передается контекст с тайм-аутом, чтобы сообщить ей, что нужно прекратить работу по истечении тайм-аута.
Код:
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
defer cancel()
select {
case <-neverReady:
fmt.Println("ready")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
Вывод:
context deadline exceeded
func WithTimeoutCause 1.21
func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
WithTimeoutCause работает как WithTimeout, но также задает причину возвращенного Context по истечении тайм-аута. Возвращенный CancelFunc не задает причину.
type CancelCauseFunc 1.20
CancelCauseFunc работает как CancelFunc, но дополнительно задает причину отмены. Эту причину можно получить, вызвав Cause для отмененного Context или любого из его производных Context.
Если контекст уже был отменен, CancelCauseFunc не задает причину. Например, если childContext является производным от parentContext:
- если parentContext отменен с причиной cause1 до отмены childContext с причиной cause2, то Cause(parentContext) == Cause(childContext) == cause1
- если childContext отменен с причиной cause2 до отмены parentContext с причиной cause1, то Cause(parentContext) == cause1 и Cause(childContext) == cause2
type CancelCauseFunc func(cause error)
type CancelFunc 1.7
CancelFunc сообщает операции, что нужно прекратить работу. CancelFunc не ожидает завершения работы. CancelFunc может одновременно вызываться несколькими горутинами. После первого вызова последующие вызовы CancelFunc ничего не делают.
type CancelFunc func()
type Context 1.7
Context передает крайний срок, сигнал отмены и другие значения через границы API.
Методы Context можно одновременно вызывать из нескольких горутин.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// after the cancel function returns.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// 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:
// }
// }
// }
//
// See https://go.dev/blog/pipelines for more examples of how to use
// a Done channel for cancellation.
Done() <-chan struct{}
// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// DeadlineExceeded if the context's deadline passed,
// or Canceled if the context was canceled for some other reason.
// After Err returns a non-nil error, successive calls to Err return the same error.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stored using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key any) any
} func Background 1.7
func Background() Context
Background возвращает ненулевой пустой Context. Он никогда не отменяется, не содержит значений и не имеет крайнего срока. Обычно он используется главной функцией, при инициализации и в тестах, а также как Context верхнего уровня для входящих запросов.
func TODO 1.7
func TODO() Context
TODO возвращает ненулевой пустой Context. Следует использовать context.TODO, когда непонятно, какой Context использовать, или он пока недоступен (поскольку окружающая функция еще не была изменена для приема параметра Context).
func WithValue 1.7
func WithValue(parent Context, key, val any) Context
WithValue возвращает производный контекст, который ссылается на родительский Context. В производном контексте значению, связанному с ключом key, соответствует val.
Используйте значения context только для данных, относящихся к запросу и передаваемых между процессами и API, а не для передачи необязательных параметров функциям.
Передаваемый ключ должен быть сравнимым и не должен иметь тип string или любой другой встроенный тип, чтобы избежать конфликтов между пакетами, использующими context. Пользователям WithValue следует определять собственные типы ключей. Чтобы избежать выделения памяти при присваивании значения интерфейсу interface{}, ключи context часто имеют конкретный тип struct{}. В качестве альтернативы статический тип экспортируемых переменных-ключей context должен быть указателем или интерфейсом.
Пример
В этом примере показано, как передать значение в контекст и как получить его, если оно существует.
Код:
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
func WithoutCancel 1.21
func WithoutCancel(parent Context) Context
WithoutCancel возвращает производный контекст, связанный с родительским контекстом, который не отменяется при отмене родительского контекста. Возвращённый контекст не возвращает Deadline или Err, а его канал Done равен nil. Вызов Cause для возвращённого контекста возвращает nil.
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/context/