Последовательность выполнения TransformBlock потока данных TPL выглядит не по порядку / асинхронно

Я следую этому пошаговому руководству по MSDN - Пошаговое руководство. Создание конвейера потока данных. Я создал сингл TransformBlock и выполнил это, выполнив Post к этому.

  // Process "The Adventurous Life of a Versatile Artist: Houdini" 
  //         by Harry Houdini.
  downloadString.Post("http://www.gutenberg.org/cache/epub/45370/pg45370.txt");

Затем после этого я называю Complete метод и есть Console.WriteLine("Press a key to exit:"); линия.

Вот полный код. Вы также можете найти его на этом этапе в этом коммите на моем репозитории github.

using System;
using System.Net.Http;
using System.Threading.Tasks.Dataflow;

namespace Palindromes.ConsoleApp
{
  class Program
  {
    static void Main(string[] args)
    {
      // 
      // Create members of the Pipeline
      //

      // Download the requested resource as a string

      var downloadString = new TransformBlock<string, string>
        ( url =>
          {
            Console.WriteLine($"Downloading from {url}...");
            string result = null;
            using (var client = new HttpClient())
            {
              // Perform a synchronous call by calling .Result
              var response = client.GetAsync(url).Result;

              if (response.IsSuccessStatusCode)
              {
                var responseContent = response.Content;

                // read result synchronously by calling .Result 
                result = responseContent.ReadAsStringAsync().Result;
                if (!string.IsNullOrEmpty(result))
                  Console.WriteLine($"Downloaded {result.Length} characters...");

              }
            }
            return result;
          }
        );

      // Process "The Adventurous Life of a Versatile Artist: Houdini" 
      //         by Harry Houdini.
      downloadString.Post("http://www.gutenberg.org/cache/epub/45370/pg45370.txt");
      downloadString.Complete();

      Console.WriteLine("Press a key to exit:");
      Console.ReadKey();
    }
  }
}

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

Ожидаемый результат

Downloading from http://www.gutenberg.org/cache/epub/45370/pg45370.txt...
Downloaded 129393 characters...
Press a key to exit:

Но вот фактический результат. (Я запускал его несколько раз с той же последовательностью Console.WriteLine выход показывая вверх.

Фактический вывод

Press a key to exit:
Downloading from http://www.gutenberg.org/cache/epub/45370/pg45370.txt...
Downloaded 129393 characters...

Почему Press a key to exit линия выполняется до TransformBlock "s Console.WriteLine Вам позвонили?

Не должен TransformBlock "s Console.WriteLine быть вызванным первым, так как я вызываю его первым, и так как это будет частью конвейера? Также у меня нет никаких async насколько я могу судить, я не знаю полностью внутреннюю работу потока данных TPL, так почему же это происходит не по порядку?

Спасибо!

2 ответа

Решение

Почему Нажмите клавишу для выхода из строки, выполняемой перед вызовом TransformBlock Console.WriteLines?

Призыв к Console.WriteLine("Press a key to exit:") происходит до TransformBlock завершил функцию преобразования. Каждый элемент размещен на TransfromBlock будет обрабатываться асинхронно с учетом вашего основного контекста.

Если вы хотите подождать, пока закончится конвейер, вам нужно будет либо заблокировать его Completion Task или же await завершение в async метод:

private static async Task MainAsync() {
    // Process "The Adventurous Life of a Versatile Artist: Houdini" 
    //         by Harry Houdini.
    downloadString.Post("http://www.gutenberg.org/cache/epub/45370/pg45370.txt");
    downloadString.Complete();

    await downloadString.Completion;
}

Post Метод вернется после того, как размещенный элемент будет сохранен во входной очереди блока, он не будет ждать его обработки.

Из документации MSDN:

Этот метод вернется, как только целевой блок решит принять или отклонить элемент, но, если иное не определено специальной семантикой целевого блока, он не ожидает фактической обработки элемента. Например, ActionBlock вернется из Post, как только он сохранит опубликованный элемент в своей входной очереди).

Элементы из входной очереди затем обрабатываются асинхронно.

Другие вопросы по тегам