@VasyaM13221

Как написать асинхронный конвеер на c#?

Доброго времени суток.
Есть такой алгоритм:
1. На вход поступает массив с файлами(streams) изображений.
2. Мы эти изображения конвертируем в другой формат(ресурсоёмко),
3. А затем кладём на удалённый сервер(может быть длительный отклик).
4. Ссылки от того куда положили возвращаем в виде коллекции тому кто нас вызвал.

Количество входных файлов нам известно сразу.
Как написать алгоритм конвеера что бы мы не ждали пока все изображения сконвертируются. Надо что бы при первом готовом мы бы сразу пытались его отправлять на удалённый сервер?

Пробовал с BlockingCollection но кажется я его не правильно использую.

UPDATED
Благодаря совету mindtester был написан такой код https://dotnetfiddle.net/fHWZ9n
Он вроде работает как надо, если есть возможность посмотрите и напишите свои замечания в комментариях к этому посту. (Кому удобно гитхаб https://gist.github.com/milovidov983/e305890bfd8fb... )
  • Вопрос задан
  • 370 просмотров
Пригласить эксперта
Ответы на вопрос 2
mindtester
@mindtester Куратор тега C#
http://iczin.su/hexagram_48
ну так IEnumerable<> и yield вам в помощь. само собой и async/await
как раз эти четыре понятия и придназначены, для упрощения написания кода, над массивами даннх, растянутыми во времени, и не известной длинны. с их помощью, большая часть кода, выглядит так, будто все входные данные предоставлены сразу, и результат вы выдаете как будто тоже сразу

ps для практики полезны примеры из MSDN (где они есть) либо на https://metanit.com/sharp/tutorial/ придется поискать. в любом случае, однозначно придется понять обсласти применения и возможности этих понятий. а тогда уже точно сложится картина, как они лягут на ваш конкретный случай

pps
Ссылки от того куда положили возвращаем в виде коллекции тому кто нас вызвал.
вот это место не очень вписывается в концепцию конвеера на IEnumerable/yield/async/await
дело в том что результирующие ссылки, вы получаете "изредка", и явно в отрыве от момента получения исходного изображения
тут несколько путей:
- именно коллекцию ссылок (json?) формировать по окончанию обработоки
- возвращать в первоисточник ссылки по одной, и формировать коллекцию там, а привязку можно делать по имени/тегу/коду/номеру изображения.. (кадра?)
- можно и асинхронно, растянуто во времени досылать json, или любую выбранную коллекцию дотнета, если на приемнике дотнет, и вы так же правите исходниками
- как вариант, если известно количество кадров изначально, можно уведомлять первоисточнк о начале пересылки хоть массива заданной размерности, в любом понятном ему формате, если конечно он так же способенн к асинхронной обработке

ppps
Пробовал с BlockingCollection
повторюсь, на сколько я уловил вашу задачу, кроме IEnumerable<>, в подобном конвеере в принципе ни че больше не нужно. ну или разве что как вы там результат решите формировать. но и для результата, даже для накопителя, из дотнетовских конструкций, того же IEnumerable<> за уши, а из него, если кончно это требуется, можно и json формировать
Ответ написан
@Jewish_Cat
Увлекаюсь C#
Посмотри DataFlow. Может это будет overkill, но для работы с изображением подходит на ура.
BigInteger result = 0;
            var inputBlock = new BufferBlock<(string,string)>(); // Блок для входных данных

            var options = new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = 200 // указываем сколько потоков будут обрабатывать наши данные
            };

            var callBalanceBlock = new TransformBlock<(string,string), BigInteger>(async x =>
            {
                BigInteger wallet = await function.CallAsync<BigInteger>(x.Item1);
                BigInteger eth = await Current.Eth.GetBalance.SendRequestAsync(x.Item1);
                TechWallets.TechEURGWallets.Add(new TechWallet
                {
                    Address = x.Item1, 
                    TokenValue = Web3.Convert.FromWei(wallet,2), 
                    EthValue = Web3.Convert.FromWei(eth, UnitConversion.EthUnit.Ether),
                    PrivateKey = x.Item2
                });
                return wallet;
            }, options); // Это главный испольнительный блок. Получает информация в виде (string,string) и отдает в формате BigInteger.

            var calc = new ActionBlock<BigInteger>(x => result += x, options); // Получает данные после работы TransformBlock, чтобы подсчитать число

            inputBlock.LinkTo(callBalanceBlock); // Прокидываем линк(соединение), что после BufferBlock, должен отрабатывать TransformBlock
            inputBlock.Completion.ContinueWith(task => callBalanceBlock.Complete());

            callBalanceBlock.LinkTo(calc); // Тоже самое, но для ActionBlock
            callBalanceBlock.Completion.ContinueWith(task => calc.Complete());

            foreach (var i in TechWallets.ListEURGTechWallets)
                inputBlock.Post(i); // Заполняем наш BufferBlock входными значениями

            inputBlock.Complete(); // Запускаем работу наших блоков

            try
            {
                calc.Completion.Wait(); // Ждем завершения работы последнего блока
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

Код станет проще намного и быстрее.
Ты можешь построить свой конвеер по типу: BufferBlock(Массив входных данных) -> TransformBlock(Тут обрабатываешь входные картинки) -> ActionBlock(Тут заливаешь картинку на сервер)
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы