Что происходит, когда использование HttpClient не ожидается

1

С кодом, похожим на

Task.Run(() =>
{
    using (var client = new HttpClient())
    {
        var responseTask = client.GetAsync(urlToInvoke);
    }
});

В такой ситуации, похоже, что GetAsync фактически не работает. Задача отменена до завершения или что здесь происходит?

Теперь, если вы немного измените ситуацию и вставьте

Task.Run(() =>
{
    using (var client = new HttpClient())
    {
        var responseTask = client.GetAsync(urlToInvoke);

        Task.Delay(5000).Wait()
    }
});

GetAsync выполняет полностью. Что здесь происходит? Является ли Task.Delay аффинитированием одной и той же Задачи, которая находится внутри responseTask конечном итоге делает этот эквивалент responseTask.Wait()?

  • 0
    ничего не происходит ... вы только что получили объект задачи, и он никогда не будет выполнен.
  • 0
    @CrazyDart тогда почему он "работает нормально" с включением Task.Delay ?
Показать ещё 2 комментария
Теги:
asynchronous
dotnet-httpclient
task-parallel-library

2 ответа

4
Лучший ответ

Вы думаете об этом неправильно. Вот псевдо версия того, что происходит внутри класса.

class HttpClient : IDisposeable
{
    private CancelationTokenSource _disposeCts;

    public HttpClient()
    {
        _disposeCts = new CancelationTokenSource();
    }

    public Task<HttpResponseMessage> GetAsync(string url)
    {
        return GetAsync(url, CancellationToken.None);
    }

    public async Task<HttpResponseMessage> GetAsync(string url, CancelationToken token)
    {
        var combinedCts =
            CancellationTokenSource.CreateLinkedTokenSource(token, _disposeCts.Token);
        var tokenToUse = combinedCts.Token;

        //... snipped code

        //Some spot where it would good to check if we have canceled yet.
        tokenToUse.ThrowIfCancellationRequested();

        //... More snipped code;

        return result;
    }

    public void Dispose()
    {
        _disposeCts.Cancel();
    }

    //... A whole bunch of other stuff.
}

Важно видеть, когда вы выходите из using блока, маркер отмены отмены отменяется.

В первом примере задача еще не закончилась, поэтому tokenToUse теперь будет бросать, если ThrowIfCancellationRequested().

Во втором примере задача уже завершена, поэтому акт отмены внутреннего токена не повлиял на задачу, которая была возвращена из-за того, что она уже достигла завершенного состояния.

Это похоже на вопрос, почему это заставляет задачу отменять.

using (var client = new HttpClient())
{
    var cts = new CancellationTokenSource()
    var responseTask = client.GetAsync(urlToInvoke, cts.Token);

    cts.Cancel();
}

но это не

using (var client = new HttpClient())
{
    var cts = new CancellationTokenSource()
    var responseTask = client.GetAsync(urlToInvoke, cts.Token);

    Task.Delay(5000).Wait()
    cts.Cancel();
}
  • 0
    Отличная иллюстрация, все мое внимание было сосредоточено на TPL, и я ни разу не подумал о последствиях утилизации HttpClient.
4

Если вы не await (или Wait) задачи, которые они не отменяют себя. Они продолжают работать, пока не достигнут одного из трех статусов:

  • RanToCompletion - успешно завершено.
  • Отменено - токен отмены отменен.
  • Faulted - Необработанное исключение произошло внутри задачи.

Однако в вашем случае, поскольку никто не ждет завершения задачи, используется область использования, которая предоставляет HttpClient. Это, в свою очередь, отменяет все клиентские задачи, client.GetAsync(urlToInvoke) в этом случае. Так что внутренняя задача async немедленно закончится и станет отменена, а внешняя задача (Task.Run) просто закончится, ничего не сделав.

Когда вы используете Task.Delay(5000).Wait() который в основном является Thread.Sleep(5000) задача имеет шанс завершить работу до завершения области использования. Однако этого режима работы следует избегать. Он блокирует поток во время Wait и может привести к взаимоблокировкам в однопоточном SynchronizationContext s. Это также скрывает возможные исключения в задаче (которая может снести приложение в более ранних версиях.Net)

Вы всегда должны ждать выполнения задач, желательно асинхронно, и, как комментирует Servy, нет причин использовать Task.Run здесь для разгрузки, потому что GetAsync является асинхронным и не будет блокировать вызывающий поток.

using (var client = new HttpClient())
{
    var response = await client.GetAsync(urlToInvoke);
}
  • 0
    Здесь также нет необходимости использовать Task.Run , поскольку сам метод является асинхронным и не будет блокировать текущий поток.
  • 0
    @ Сервис действительно. обновлено.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню