У меня есть консольное приложение, написанное с использованием C#
поверх платформы Core .NET 2.2.
Мое приложение позволяет мне запускать длительные административные задания с помощью планировщика задач Windows.
Одно из заданий администратора выполняет вызов веб-API, который загружает множество файлов перед их отправкой в хранилище BLOB-объектов Azure. Вот логические шаги, которые мой код должен будет выполнить, чтобы выполнить работу.
- Вызовите удаленный API, который ответит сообщением Mime, где каждое сообщение представляет файл.
- Разберите сообщения Mime и преобразуйте каждое сообщение в
MemoryStream
, создав коллекцию MemoryStream.
Когда у меня будет коллекция из более чем 1000 MemoryStream
, я хочу записать каждый Stream
в хранилище BLOB-объектов Azure. Поскольку запись в удаленное хранилище выполняется медленно, я надеюсь, что смогу выполнить каждую итерацию записи, используя свой собственный процесс или поток. Это позволит мне одновременно параллельно выполнять более 1000+ потоков вместо того, чтобы ждать результата каждой операции записи. Каждый поток будет отвечать за регистрацию любых ошибок, которые могут возникнуть в процессе записи/загрузки. Любые зарегистрированные ошибки будут обработаны с использованием другого задания, поэтому мне не нужно беспокоиться о повторных попытках.
Насколько я понимаю, вызов кода, который асинхронно записывает/загружает поток, будет делать именно это. Другими словами, я бы сказал: «Есть Stream
выполнить его и работать столько, сколько потребуется. Меня не волнует результат, пока задача выполнена».
Во время тестирования я обнаружил, что мое понимание вызова async
несколько неверно. У меня сложилось впечатление, что при вызове метода, определенного с помощью async
, он будет выполняться в фоновом потоке/рабочем, пока этот процесс не будет завершен. Но мое понимание потерпело неудачу, когда я тестировал код. Мой код показал мне, что без добавления ключевого слова await
код async
на самом деле никогда не выполняется. В то же время, когда добавляется ключевое слово await
, код будет ждать, пока процесс завершит выполнение, прежде чем продолжить. Другими словами, добавление await
для моих нужд лишает смысла асинхронный вызов метода.
Вот урезанная версия моего кода для объяснения того, что я пытаюсь выполнить.
public async Task Run()
{
// This gets populated after calling the web-API and parsing out the result
List<Stream> files = new List<MemoryStream>{.....};
foreach (Stream file in files)
{
// This code should get executed in the background without having to await the result
await Upload(file);
}
}
// This method is responsible of upload a stream to a storage and log error if any
private async Task Upload(Stream stream)
{
try
{
await Storage.Create(file, GetUniqueName());
}
catch(Exception e)
{
// Log any errors
}
}
Из приведенного выше кода вызов await Upload(file);
работает и загрузит файл, как и ожидалось. Однако, поскольку я использую await
при вызове метода Upload()
, мой цикл НЕ перейдет к следующей итерации, пока код загрузки не завершится. В то же время, убрав ключевое слово await
, цикл не ждет процесса загрузки, но Stream никогда фактически не пишет в хранилище, как будто я никогда не вызывал код.
Как я могу выполнять несколько методов Upload
параллельно, чтобы у меня был один поток, работающий для каждой загрузки в фоновом режиме?
Task.WhenAll()
в этом случае? Запускает ли он все задачи одновременно параллельно, объединяет ли он задачи в группы и распределяет группы параллельно или выполняет одну за другой в конвейере? 05.02.2019Parallel.ForEach
. И по моему опытуTask.WhenAll
запускает все задачи сразу. Это где-нибудь задокументировано? 05.02.2019Task.WhenAll
— это асинхронный метод, который возвращает значение, когда одна задача не удалась или все завершились успешно. Если вы ждете этого, выполнение приостанавливается до тех пор, пока задача не будет завершена. 05.02.2019await Task.WhenAll(Enumerable.Range(0, Math.Pow(2,10)).Select(_ => httpClient.GetAsync("https://google.com")))
не будет корректно планировать рабочую нагрузку. Будет ли он? 06.02.2019