Почему этот код не работает при выполнении TPL/Tasks?

Я использую System.Net.Http для использования сетевых ресурсов. При работе в одном потоке он отлично работает. Когда я запускаю код через TPL, он зависает и никогда не завершается до тех пор, пока не будет достигнут тайм-аут.

Что происходит, так это то, что все потоки заканчиваются в очереди sendTask.Result. Я не уверен, чего они ждут, но я предполагаю, что это что-то в HttpClient.

Сетевой код:

using (var request = new HttpRequestMessage(HttpMethod.Get, "http://google.com/"))
{
    using (var client = new HttpClient())
    {
        var sendTask = client.SendAsync
              (request, HttpCompletionOption.ResponseHeadersRead);
        using (var response = sendTask.Result)
        {
            var streamTask = response.Content.ReadAsStreamAsync();
            using (var stream = streamTask.Result)
            {
                // problem occurs in line above
            }
        }
    }
}

Код TPL, который я использую, выглядит следующим образом. Метод Do содержит именно код выше.

var taskEnumerables = Enumerable.Range(0, 100);
var tasks = taskEnumerables.Select
            (x => Task.Factory.StartNew(() => _Do(ref count))).ToArray();
Task.WaitAll(tasks);

Я попробовал несколько разных планировщиков, и единственный способ заставить его работать - написать планировщик, который ограничивает количество выполняемых задач до 2 или 3. Однако даже это иногда не выполняется.

Я бы предположил, что моя проблема в HttpClient, но для моей жизни я не вижу никакого общего состояния в своем коде. У кого-нибудь есть идеи?

Спасибо, Erick

Ответ 1

Наконец-то я нашел проблему. Проблема заключалась в том, что HttpClient выдает свои собственные дополнительные задачи, поэтому одна задача, которую я запускаю, может на самом деле заканчиваться на 5 или более задач.

Планировщик был настроен с ограничением количества задач. Я запустил задачу, из-за которой количество запущенных задач достигло максимального предела. Затем HttpClient попытался запустить свои собственные задачи, но поскольку предел был достигнут, он блокировался до тех пор, пока количество задач не снизилось, чего, конечно, никогда не было, поскольку они ожидали завершения моих задач. Привет, тупик.

Мораль истории:

  • Задачи могут быть глобальным ресурсом
  • Часто возникают не очевидные взаимозависимости между задачами
  • Планировщики нелегко работать с
  • Не предполагайте, что вы управляете планировщиками или количеством задач.

В результате я использовал другой метод для дросселирования количества подключений.

Эрик