Беспорядок контекста Голанга относительно отмены - PullRequest
0 голосов
/ 14 октября 2018
package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func myfunc(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Ctx is kicking in with error:%+v\n", ctx.Err())
            return
        default:
            time.Sleep(15 * time.Second)
            fmt.Printf("I was not canceled\n")
            return
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(
        context.Background(),
        time.Duration(3*time.Second))
    defer cancel()

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        myfunc(ctx)
    }()

    wg.Wait()
    fmt.Printf("In main, ctx err is %+v\n", ctx.Err())
}

У меня есть приведенный выше фрагмент кода, который выводит на печать вывод, подобный этому

I was not canceled
In main, ctx err is context deadline exceeded

Process finished with exit code 0

Я понимаю, что context истекает через 3 секунды и, следовательно, дает ожидаемую ошибку, когда япозвоните ctx.Err() в конце.Я также понимаю, что в моих myfunc однократных select совпадениях на корпусе для default, он не будет совпадать на done.Чего я не понимаю, так это как заставить go func myfunc прерваться за 3 секунды, используя контекстную логику.По сути, это не прекратится через 3 секунды, поэтому я пытаюсь понять, как Голанг ctx может помочь мне с этим?

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Если вы хотите использовать функцию тайм-аут и отмену из контекста, то ctx.Done() необходимо обрабатывать синхронно.

Пояснение от https://golang.org/pkg/context/#Context

Done возвращает канал, который закрыт, когда работа, выполненная от имени этого контекста, должна быть отменена.Готово может вернуть ноль, если этот контекст никогда не может быть отменен.Последовательные вызовы Done возвращают одно и то же значение.

Таким образом, в основном <-ctx.Done() будет вызываться при двух условиях:

  1. , когда время ожидания контекста превышает
  2. когда контекст отменяется силой

И когда это происходит, ctx.Err() никогда не будет nil.Затем мы можем выполнить некоторую проверку объекта ошибки, чтобы увидеть, был ли контекст отменен принудительно или превысил время ожидания.

Пакет контекста предоставляет объект двух ошибок, context.DeadlineExceeded и context.Timeout, эти два помогут намлегко понять, почему выполняется <-ctx.Done().


Пример использования сценария: превышено время ожидания контекста

В тесте мы попытаемся отменить контекст до истечения времени ожидания, поэтому <-ctx.Done() будет выполнено.

ctx, cancel := context.WithTimeout(
    context.Background(),
    time.Duration(3*time.Second))
defer cancel()

go func(ctx context.Context) {
    defer cancel()

    // simulate a process that takes 2 second to complete
    time.Sleep(2 * time.Second)
}(ctx)

select {
case <-ctx.Done():
    switch ctx.Err() {
    case context.DeadlineExceeded:
        fmt.Println("context timeout exceeded")
    case context.Canceled:
        fmt.Println("cancel the context by force")
    }
}

Вывод:

$ go run test.go 
cancel the context by force

Пример использования сценария: превышено время ожидания контекста

В этом сценариимы делаем процесс дольше, чем время ожидания контекста, поэтому в идеале <-ctx.Done() также будет выполняться.

ctx, cancel := context.WithTimeout(
    context.Background(),
    time.Duration(3*time.Second))
defer cancel()

go func(ctx context.Context) {
    defer cancel()

    // simulate a process that takes 4 second to complete
    time.Sleep(4 * time.Second)
}(ctx)

select {
case <-ctx.Done():
    switch ctx.Err() {
    case context.DeadlineExceeded:
        fmt.Println("context timeout exceeded")
    case context.Canceled:
        fmt.Println("cancel the context by force")
    }
}

Вывод:

$ go run test.go 
context timeout exceeded
0 голосов
/ 14 октября 2018

В вашем for ... select есть 2 случая: case <-ctx.Done(): и default:.Когда ваш код достигает значения select, он входит в случай default, потому что контекст еще не отменен, где он спит в течение 15 секунд, а затем возвращается, прерывая ваш цикл.(другими словами, он не блокирует / ожидает отмены вашего контекста)

Если вы хотите, чтобы ваш код делал то, что вы описываете, вам нужно, чтобы в select были случаи отмены контекста и установленное вами время ожидания.

for {
  select {
  case <-ctx.Done(): // context was cancelled
    fmt.Printf("Ctx is kicking in with error:%+v\n", ctx.Err())
    return
  case <-time.After(15 * time.Second): // 15 seconds have elapsed
    fmt.Printf("I was not canceled\n")
    return
  }
}

Теперь ваш код будет блокироваться при достижении значения select, вместо того, чтобы вводить случай default и прерывать цикл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...