Одновременный запуск задач с ограничением количества ошибок

Нужен пул из n работников и общая отмена при ошибке номер m+1. Уже запущенные задачи должны поддерживать context, иначе гарантировать их остановку нельзя.

Короткий ответ

Что ответить на собеседовании

Нужен пул из n работников и общая отмена при ошибке номер m+1. Уже запущенные задачи должны поддерживать context, иначе гарантировать их остановку нельзя.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо написать функцию, которая распределяет выполнение заданных задач по n горутинам, независимо от общего количества задач. При этом процесс должен быть прерван, как только количество возникших ошибок превысит порог m, и при этом не должно оставаться работающих горутин. Если количество ошибок не превышает m, все задачи должны быть выполнены полностью.

Ответ

Нужен пул из n работников и общая отмена при ошибке номер m+1. Уже запущенные задачи должны поддерживать context, иначе гарантировать их остановку нельзя.

Явно зададим Task как функцию, возвращающую error и учитывающую отмену. Возвращаем число ошибок; при допустимом количестве задачам дают закончиться.

import (
    "context"
    "errors"
    "sync"
)

func RunTasks(parent context.Context, tasks []func(context.Context) error,
    n, m int) (int, error) {
    if n < 1 || m < 0 { return 0, errors.New("invalid limits") }
    ctx, cancel := context.WithCancel(parent)
    defer cancel()
    var mu sync.Mutex
    var wg sync.WaitGroup
    next, failures := 0, 0
    for i := 0; i < n; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                mu.Lock()
                if ctx.Err() != nil || next == len(tasks) {
                    mu.Unlock()
                    return
                }
                task := tasks[next]
                next++
                mu.Unlock()
                if err := task(ctx); err != nil {
                    mu.Lock()
                    failures++
                    if failures > m { cancel() }
                    mu.Unlock()
                }
            }
        }()
    }
    wg.Wait()
    if failures > m { return failures, errors.New("error limit exceeded") }
    return failures, parent.Err()
}

После превышения порога новые задания не выдаются; уже выданные могут закончиться с дополнительными ошибками. Возврат происходит только после остановки всех работников. Задачи должны быть ненулевыми, не паниковать и завершаться при отмене: произвольную функцию, игнорирующую context, Go принудительно безопасно не останавливает.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее