Обёртка может ограничить время ожидания вызывающего, но не принудительно остановить функцию без поддержки отмены. Буфер на один результат не даст работнику зависнуть на поздней отправке.
Декоратор с ограничением времени выполнения и логированием для медленной функции
Обёртка может ограничить время ожидания вызывающего, но не принудительно остановить функцию без поддержки отмены. Буфер на один результат не даст работнику зависнуть на поздней отправке.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Требуется написать обёртку вокруг функции, которая может работать неопределённо долго и возвращать целочисленное значение. Тело этой функции менять нельзя – представим, что внутри происходит сетевой запрос или другая тяжёлая операция. Обёртка должна запускать функцию с установленным тайм‑аутом (например, одной секунды): если функция успевает завершиться за отведённое время – возвращаем её результат, иначе – возвращаем ошибку, при этом полученный результат игнорируем. Кроме того, необходимо измерять длительность выполнения функции и выводить её в лог. Сигнатуру обёртки разрешено изменить.
package main
import (
"fmt"
"math/rand"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
// Есть функция, работающая неопределённо долго и возвращающая число.
// Её тело нельзя изменить (представим, что внутри сетевой запрос).
func unpredictableFunc() int64 {
rnd := rand.Int63n(5000)
time.Sleep(time.Duration(rnd) * time.Millisecond)
return rnd
}
// Нужно изменить функцию обёртку, которая будет работать с заданным таймаутом (например, 1 секунду).
// Если "длинная" функция отработала за это время — отлично, возвращаем результат.
// Если нет — возвращаем ошибку. Результат работы в этом случае нам не важен.
// Дополнительно нужно измерить, сколько выполнялась эта функция (просто вывести в лог).
// Сигнатуру функции обёртки менять можно.
func predictableFunc() int64 {
return unpredictableFunc()
}
func main() {
fmt.Println("Started")
fmt.Println(predictableFunc())
}
Ответ
Обёртка может ограничить время ожидания вызывающего, но не принудительно остановить функцию без поддержки отмены. Буфер на один результат не даст работнику зависнуть на поздней отправке.
С импортами context, log и time:
func predictableFunc(parent context.Context, timeout time.Duration) (int64, error) {
ctx, cancel := context.WithTimeout(parent, timeout)
defer cancel()
if err := ctx.Err(); err != nil { return 0, err }
result := make(chan int64, 1)
go func() {
started := time.Now()
value := unpredictableFunc()
log.Printf("function duration: %s", time.Since(started))
result <- value
}()
select {
case <-ctx.Done(): return 0, ctx.Err()
case value := <-result:
if err := ctx.Err(); err != nil { return 0, err }
return value, nil
}
}
Вызовите её с context.Background() и time.Second. Лог фиксирует фактическое завершение функции, даже после тайм-аута. Если операция зависнет навсегда, останется горутина; гарантия принудительной остановки требует поддержки context или изоляции в управляемом процессе. Ограничивайте число таких запусков.