Пакет context
Обзор
Пакет context определяет тип Context, который переносит дедлайны, сигналы отмены и другие значения, относящиеся к запросу, через границы API и между процессами.
Входящие запросы на сервер должны создавать контекст Context, а исходящие вызовы серверам должны принимать контекст. Цепочка вызовов функций между ними должна передавать контекст, необязательно заменяя его производным контекстом, созданным с помощью WithCancel, WithDeadline, WithTimeout или WithValue. При отмене контекста все контексты, полученные из него, также отменяются.
Функции WithCancel, WithDeadline и WithTimeout принимают контекст (родительский) и возвращают производный контекст (дочерний) и функцию CancelFunc. Вызов CancelFunc отменяет дочерний контекст и его дочерние контексты, удаляет ссылку родительского контекста на дочерний и останавливает все связанные таймеры. Если не вызвать CancelFunc, дочерний контекст и его потомки будут утечкой до тех пор, пока родительский контекст не будет отменён или таймер не сработает. Инструмент go vet проверяет, что CancelFuncs используются по всем путям управления потоком.
Программы, использующие контексты, должны следовать этим правилам, чтобы поддерживать согласованность интерфейсов между пакетами и позволять инструментам статического анализа проверять распространение контекстов:
Не храните контексты внутри структуры; вместо этого передавайте контекст explicitly каждой функции, которая его требует. Контекст должен быть первым параметром, обычно с именем ctx:
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
Не передавайте nil контекст, даже если функция это допускает. Передайте context.TODO, если вы не уверены, какой контекст использовать.
Используйте значения контекста только для данных, относящихся к запросу, которые передаются через процессы и API, а не для передачи необязательных параметров в функции.
Один и тот же контекст может передаваться функциям, выполняющимся в разных горутинах; контексты безопасны для одновременного использования несколькими горутинами.
См. https://blog.golang.org/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 becoming done,
// so to be sure that ctx is actually done 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()
Вывод:
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", ":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 используется для определения функции, которая объединяет сигналы отмены двух контекстов.
Код:
// 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 возвращает не-nil ошибку, объясняющую, почему 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 возвращает копию parent с новым каналом Done. Возвращаемый канал Done контекста закрывается, когда вызывается возвращаемая функция cancel или когда закрывается родительский канал Done, в зависимости от того, что произойдёт первым.
Отмена этого контекста освобождает ресурсы, связанные с ним, поэтому код должен вызвать cancel как можно скорее после завершения операций, выполняющихся в этом контексте.
Пример
Этот пример демонстрирует использование контекста с возможностью отмены, чтобы предотвратить утечку горутин. К концу функции пример горутина, запущенная 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)
WithCancelCause ведет себя как WithCancel, но возвращает CancelCauseFunc вместо CancelFunc. Вызов cancel с не-nil ошибкой (причиной) записывает эту ошибку в 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] закрывается при истечении дедлайна, при вызове возвращаемой функции cancel или при закрытии родительского канала Done, в зависимости от того, что произойдёт первым.
Отмена этого контекста освобождает ресурсы, связанные с ним, поэтому код должен вызвать cancel как можно скорее после завершения операций, выполняющихся в этом контексте.
Пример
В этом примере передаётся контекст с произвольным дедлайном, чтобы сказать блокирующей функции, что она должна отказаться от своей работы как можно скорее, когда дойдёт до него.
Код:
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, но также устанавливает причину возвращаемого контекста, когда дедлайн истекает. Возвращаемая CancelFunc не устанавливает причину.
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() // 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, но также устанавливает причину возвращаемого контекста, когда таймаут истекает. Возвращаемая CancelFunc не устанавливает причину.
тип CancelCauseFunc 1.20
CancelCauseFunc ведет себя как CancelFunc, но дополнительно устанавливает причину отмены. Эту причину можно получить, вызвав Cause в отменённом контексте или в любом из полученных из него контекстов.
Если контекст уже был отменён, CancelCauseFunc не устанавливает причину. Например, если childContext получен из parentContext:
- Если родительский контекст отменён с причиной cause1 до того, как дочерний контекст отменён с причиной cause2, то Cause(родительский контекст) == Cause(дочерний контекст) == cause1
- Если дочерний контекст отменён с причиной cause2 до того, как родительский контекст отменён с причиной cause1, то Cause(родительский контекст) == cause1 и Cause(дочерний контекст) == cause2
type CancelCauseFunc func(cause error)
тип CancelFunc 1.7
CancelFunc сообщает операции о необходимости прекращения работы. CancelFunc не ожидает завершения работы. CancelFunc может вызываться несколькими горутинами одновременно. После первого вызова последующие вызовы CancelFunc не выполняют никаких действий.
type CancelFunc func()
тип 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://blog.golang.org/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:
// Canceled if the context was canceled
// or DeadlineExceeded if the context's deadline passed.
// 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
} функция Background 1.7
func Background() Context
Background возвращает непустой контекст Context. Он никогда не отменяется, не содержит значений и не имеет срока действия. Обычно используется в основной функции, инициализации и тестах, а также в качестве контекста верхнего уровня для входящих запросов.
функция TODO 1.7
func TODO() Context
TODO возвращает непустой контекст Context. Код должен использовать context.TODO, когда неясно, какой контекст использовать или он ещё недоступен (потому что окружающая функция ещё не расширена для принятия параметра Context).
функция WithValue 1.7
func WithValue(parent Context, key, val any) Context
WithValue возвращает копию parent, в которой значение, связанное с ключом, равно val.
Используйте значения контекста только для данных, относящихся к запросу, которые передаются через процессы и API, а не для передачи необязательных параметров функциям.
Предоставленный ключ должен быть сравнимым и не должен иметь тип string или любой другой встроенный тип, чтобы избежать коллизий между пакетами, использующими контекст. Пользователи WithValue должны определять свои собственные типы для ключей. Чтобы избежать выделения при присвоении интерфейсу, ключи контекста часто имеют конкретный тип 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
функция WithoutCancel 1.21
func WithoutCancel(parent Context) Context
WithoutCancel возвращает копию parent, которая не отменяется при отмене parent. Возвращаемый контекст не содержит срок действия или ошибку, а канал Done равен null. Вызов Cause для возвращаемого контекста возвращает null.
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/context/