Отмена так обязательна для контекста? - PullRequest
0 голосов
/ 22 января 2020

Получено от go vet:

функция отмены, возвращаемая context.WithTimeout должна вызываться, а не отбрасываться, чтобы избежать утечки контекста

код выглядит следующим образом:

func foo(ctx context.Context) {
  for ... some loop {
    c, _ := context.WithTimeout(ctx, time.Second)
    err = enc.RequestWithContext(c, "some", someRequest, &response)
    if err != nil {
      continue
    }
    if response.Code == -1 {
      break
    }
  }
}

Так какой смысл отменять его здесь после запроса? Просто чтобы сделать vet счастливым? Этот контекст в любом случае уже завершен или истек срок его действия.

func foo(ctx context.Context) {
  for ... some loop {
    c, cancel := context.WithTimeout(ctx, time.Second)
    err = enc.RequestWithContext(c, "some", someRequest, &response)
    cancel()

    if err != nil {
      continue
    }
    if response.Code == -1 {
      break
    }
  }
}

Ответы [ 2 ]

5 голосов
/ 22 января 2020

Является ли отмена обязательной для контекста?

Да, здесь нет никаких аргументов.

С go doc context:

Не удалось Вызовите CancelFun c Утечка дочернего элемента и его дочерних элементов до тех пор, пока родитель не будет отменен или не сработает таймер. Инструмент go vet проверяет, используются ли функции CancelFuncs на всех путях потока управления.

Отказ от отмены настолько плох, что даже go vet предупреждает вас об этом.

4 голосов
/ 22 января 2020
c, cancel := context.WithTimeout(ctx, time.Second)
err = enc.RequestWithContext(c, "some", someRequest, &response)
cancel()

enc.RequestWithContext() может возвращаться нормально, до истечения 1-секундного перерыва, и ресурсы, используемые контекстом, будут немедленно освобождены, только если вы позвоните cancel().

cancel() идемпотентно Вы можете вызывать его несколько раз одновременно с несколькими программами. Последующие звонки oop. Если для контекста уже истекло время ожидания или был вызван cancel(), то повторный вызов этого не повредит.

Убедиться в том, что отмена вызвана, проще всего с помощью defer, что-то вроде этого:

c, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
err = enc.RequestWithContext(c, "some", someRequest, &response)

Но будьте осторожны, когда этот фрагмент находится внутри al oop: отложенные функции выполняются только тогда, когда завершается окружающая функция, а не когда начинается следующая итерация l oop. В таких случаях вы должны использовать литерал функции или именованную функцию, чтобы убедиться, что отложенные вызовы выполняются до начала следующей итерации. Подробнее см. `defer` в l oop - что будет лучше?

Возможное решение здесь будет:

for ... some loop {
    res := func() bool {
        c, cancel := context.WithTimeout(ctx, time.Second)
        defer cancel()
        err = enc.RequestWithContext(c, "some", someRequest, &response)
        if err != nil {
            return false
        }
        if response.Code == -1 {
            return true
        }
    }()
    if res {
        break
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...