Повторное использование родительского контекста с context.WithTimeout в go? - PullRequest
2 голосов
/ 17 апреля 2020

Повторное использование родительского контекста с context.WithTimeout с новым тайм-аутом

Привет, я новичок в go. Мне было интересно, если можно повторно использовать родительский контекст для создания нескольких context.withTimeout (). Обоснованием было бы то, где я должен вызывать несколько сетевых запросов последовательно и хотел бы установить таймаут для каждого запроса одновременно, используя контекст родителя.

Обоснование

Когда контекст родителя отменяется, все сделанные запросы также отменяются.

Проблема

В приведенном ниже коде показан пример, в котором LongProcess является запросом сети. Однако контекст закрывается, прежде чем второй вызов LongProcess может быть выполнен с помощью context deadline exceeded.

В документации withDeadline говорится The returned context's Done channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context's Done channel isclosed, whichever happens first.

Так что, если это так, есть ли способ, где я могу сбросить таймер для withTimeout? Или мне нужно создавать новый контекст context.Background() для каждого запроса? Это будет означать, что родительский контекст не будет передан. : (


// LongProcess refers to a long network request
func LongProcess(ctx context.Context, duration time.Duration, msg string) error {
    c1 := make(chan string, 1)
    go func() {
        // Simulate processing
        time.Sleep(duration)
        c1 <- msg
    }()

    select {
    case m := <-c1:
        fmt.Println(m)
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func main() {
    ctx := context.Background()
    t := 2 * time.Second

    ctx, cancel := context.WithTimeout(ctx, t)
    defer cancel()

    // Simulate a 2 second process time
    err := LongProcess(ctx, 2*time.Second, "first process")
    fmt.Println(err)

    // Reusing the context.
    s, cancel := context.WithTimeout(ctx, t)
    defer cancel()

    // Simulate a 1 second process time
    err = LongProcess(s, 1*time.Second, "second process")
    fmt.Println(err) // context deadline exceeded
}

1 Ответ

1 голос
/ 17 апреля 2020

Похоже, первый вызов context.WithTimeout затеняет родительский контекст ctx. Более поздний процесс повторно использует этот уже отмененный контекст, отсюда и ошибка. Вы должны повторно использовать родительский. Вот обновленный пример:

func main() {
    // Avoid to shadow child contexts
    parent := context.Background()
    t := 2 * time.Second

    // Use the parent context.
    ctx1, cancel := context.WithTimeout(parent, t)
    defer cancel()

    err := LongProcess(ctx1, 2*time.Second, "first process")
    fmt.Println(err)

    // Use the parent context not the canceled one.
    ctx2, cancel := context.WithTimeout(parent, t)
    defer cancel()

    err = LongProcess(ctx2, 1*time.Second, "second process")
    fmt.Println(err)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...