Безопасно ли не ждать всех заданий? - PullRequest
0 голосов
/ 11 января 2019

Скажите, у меня есть следующий метод действия:

[HttpPost]
public async Task<IActionResult> PostCall()
{
    var tasks = new List<Task<bool>>();
    for (int i = 0; i < 10; i++)
        tasks.Add(Manager.SomeMethodAsync(i));

    // Is this line necessary to ensure that all tasks will finish successfully?
    Task.WaitAll(tasks.ToArray());

    if (tasks.Exists(x => x.Result))
        return new ObjectResult("At least one task returned true");
    else
        return new ObjectResult("No tasks returned true");
}

Требуется ли Task.WaitAll(tasks.ToArray()) для успешного завершения всех задач? Будут ли задачи, для которых Result произошло , а не для доступа к Exists, успешно завершат свое выполнение в фоновом режиме? Или есть вероятность того, что некоторые задачи (которые не ожидались) будут отброшены, поскольку они не будут присоединены к запросу? Есть ли лучшая реализация, по которой я скучаю?

Ответы [ 2 ]

0 голосов
/ 11 января 2019

В соответствии с предоставленной вам реализацией, вызов Task.WaitAll блокирует вызывающий поток до тех пор, пока все задачи не будут завершены. Он только перейдет к следующей строке и выполнит проверку Exists после того, как это произошло. Если вы удалите Task.WaitAll, то проверка Exists приведет к тому, что вызывающий поток будет блокировать каждую задачу по порядку; то есть сначала блокируется на tasks[0]; если он возвращает false, то он блокируется на tasks[1], затем tasks[2] и так далее. Это нежелательно, поскольку не позволяет вашему методу завершиться досрочно, если задачи завершаются не по порядку.

Если вам нужно только подождать, пока какая-либо задача не вернет значение true, тогда вы можете использовать Task.WhenAny. Это заставит ваш асинхронный метод возобновить работу, как только любая задача будет завершена. Затем вы можете проверить, соответствует ли оно значению true, и сразу же вернуть результат; в противном случае вы продолжаете повторять процесс для оставшегося набора задач, пока не останется ни одного.

Если ваш код выполнялся как приложение (WPF, WinForms, Console), то оставшиеся задачи продолжали бы выполняться в пуле потоков до завершения, пока приложение не будет закрыто. Потоки пула потоков являются фоновыми потоками, поэтому они не будут поддерживать процесс в активном состоянии, если все потоки переднего плана были прерваны (например, потому что все окна были закрыты).

Поскольку вы запускаете веб-приложение, вы рискуете перезапустить пул приложений до того, как задачи будут завершены. Неожиданные задачи запускаются и забываются и поэтому не отслеживаются средой выполнения. Чтобы этого не происходило, вы можете зарегистрировать их во время выполнения с помощью метода HostingEnvironment.QueueBackgroundWorkItem, как предлагается в комментариях.

[HttpPost]
public async Task<IActionResult> PostCall()
{
    var tasks = Enumerable
        .Range(0, 10)
        .Select(Manager.SomeMethodAsync)
        .ToList();

    foreach (var task in tasks)
        HostingEnvironment.QueueBackgroundWorkItem(_ => task);

    while (tasks.Any())
    {
        var readyTask = await Task.WhenAny(tasks);
        tasks.Remove(readyTask);
        if (await readyTask)
            return new ObjectResult("At least one task returned true");
    }

    return new ObjectResult("No tasks returned true");
}
0 голосов
/ 11 января 2019

Да, задачи не гарантированно завершены, если что-то их не ждет (с чем-то вроде ожидания)

В вашем случае основное изменение, которое вы должны сделать, это сделать Task.WaitAll

await Task.WhenAll(tasks);

Так что это на самом деле асинхронный. Если вы просто хотите дождаться возврата задания a , используйте вместо него WhenAny.

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