Асинхронное ожидание завершения задания <T>с таймаутом - PullRequest
328 голосов
/ 21 ноября 2010

Я хочу дождаться завершения Задачи с некоторыми специальными правилами: Если он не завершился через X миллисекунд, я хочу показать сообщение пользователю. И если он не завершился через Y миллисекунд, я хочу автоматически запросить отмену .

Я могу использовать Task.ContinueWith для асинхронного ожидания завершения задачи (т.е. запланировать действие, которое будет выполнено после ее завершения), но это не позволяет указать время ожидания. Я могу использовать Task.Wait для синхронного ожидания завершения задачи с тайм-аутом, но это блокирует мой поток. Как я могу асинхронно ожидать завершения задачи с тайм-аутом?

Ответы [ 12 ]

0 голосов
/ 12 сентября 2018

Я почувствовал, что задача Task.Delay() и CancellationTokenSource в других ответах довольно много для моего варианта использования в тесном сетевом цикле.

И хотя Создание задания Джо Хоага.Метод TimeoutAfter в блогах MSDN был вдохновляющим, я немного устал от использования TimeoutException для управления потоком по той же причине, что и выше, потому что тайм-ауты ожидаются чаще, чем нет.

Так что я пошел сэто, что также обрабатывает оптимизации, упомянутые в блоге:

public static async Task<bool> BeforeTimeout(this Task task, int millisecondsTimeout)
{
    if (task.IsCompleted) return true;
    if (millisecondsTimeout == 0) return false;

    if (millisecondsTimeout == Timeout.Infinite)
    {
        await Task.WhenAll(task);
        return true;
    }

    var tcs = new TaskCompletionSource<object>();

    using (var timer = new Timer(state => ((TaskCompletionSource<object>)state).TrySetCanceled(), tcs,
        millisecondsTimeout, Timeout.Infinite))
    {
        return await Task.WhenAny(task, tcs.Task) == task;
    }
}

Пример использования таков:

var receivingTask = conn.ReceiveAsync(ct);

while (!await receivingTask.BeforeTimeout(keepAliveMilliseconds))
{
    // Send keep-alive
}

// Read and do something with data
var data = await receivingTask;
0 голосов
/ 11 февраля 2015

Если вы используете BlockingCollection для планирования задачи, производитель может запустить потенциально долго выполняющуюся задачу, а потребитель может использовать метод TryTake, в который встроен маркер времени ожидания и отмены.

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