ASYNC И AWAIT – КЛЮЧ К ПОВЫШЕНИЮ ОТЗЫВЧИВОСТИ ПРИЛОЖЕНИЯ
Руслан Гибадуллин (КНИТУ-КАИ)

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

✅ ИСХОДНЫЕ КОДЫ https://bitbucket.org/landwatersun/foru … 210302.zip
✅ РЕКОМЕНДУЕМАЯ КНИГА C#. Справочник. Полное описание языка | Албахари Бен, Албахари Джозеф https://www.ozon.ru/context/detail/id/145563645/

🔹 Первый ингредиент – класс Task
На языке C#, как и в любом другом языке, есть низкоуровневый инструмент для организации параллельной обработки, который называется поток. Он имеет ограничения.
1) Несмотря на простоту передачи данных запускаемому потоку, не существует простого способа получить “возвращаемое значение” обратно из потока.  Для этого потребуется предусмотреть какое-то разделяемое поле. И если операция сгенерирует исключение, то его перехват и распространение будут сопряжены с аналогичными трудностями.
2) После завершения потоку нельзя сообщить о том, что необходимо запустить что-то еще; взамен к нему придется присоединяться с помощью метода Join в целях ожидания завершения его работы (блокируя собственный поток в процессе).
Указанные ограничения препятствуют реализации мелкомодульного параллелизма; другими словами, они затрудняют формирование более крупных параллельных операций за счет комбинирования мелких операций. В свою очередь возникает более высокая зависимость от ручной синхронизации (блокировки, выдачи сигналов и т.д.), это вызывает некоторые трудности в ходе параллельного программирования.
Прямое применение потоков также оказывает влияние на производительность, например, если требуется запустить сотни или тысячи параллельных операций с интенсивным вводом-выводом, то подход на основе потоков повлечет за собой затраты памяти на накладные расходы, связанные с потоками.
Класс Task помогает решить все проблемы, упомянутые в отношении Thread. В сравнении с потоком тип Task это абстракция более высокого уровня. Задачи поддерживают возможность композиции, т.е. их можно соединять вместе с использованием продолжения. Также они могут работать с пулом потоков в целях снижения задержки во время запуска.
С помощью класса TaskCompletionSource можно создать задачу из любой операции, которая начинается и через некоторое время заканчивается. Он работает путем предоставления “подчиненной” задачи, которая управляется вручную указанием, когда операция завершилась или отказала. Это идеально для работы с интенсивным вводом-выводом: вы получаете все преимущества задач (с их возможностями передачи возвращаемых значений, исключений и признаков продолжения), не блокируя поток на период выполнения операции.

🔹 Второй ингредиент – ключевые слова async и await
Ключевые слова async и await появились в C# пятой версии. Они позволяют писать асинхронный код, обладающий той же самой структурой и простотой, что и синхронный код, а также устранять необходимость во вспомогательном коде, который присущ асинхронному программированию.
Ключевое слово await упрощает присоединение признаков продолжения. Встретив выражение await, процесс выполнения обычно производит возврат в вызывающий код . Но перед возвратом исполняющая среда присоединяет к ожидающей задаче признак продолжения, который гарантирует, что когда задача завершится, управление перейдет обратно в метод и продолжит с места, где оно его оставило. Если задача отказывает, тогда ее исключение генерируется повторно, а в противном случае выражению await присваивается возвращаемое значение задачи.
Модификатор async, добавляемый к методу сообщает компилятору о необходимости трактовать await как ключевое слово, а не идентификатор. Методы с модификатором async называются асинхронными функциями, т.к. сами они обычно асинхронны.
При это следует отметить очень важный момент. Когда запускается такой метод в обработчике события элемента пользовательского интерфейса, выполнение продолжается до выражения await, после чего управление возвращается в цикл сообщений, освобождая пользовательский интерфейс для реагирования на дальнейшие события. Однако расширение выражения await, сделанное компилятором, гарантирует, что перед возвращением продолжение настроено так, чтобы выполнение возобновлялось там, где оно было прекращено до завершения задачи. И поскольку ожидание с помощью await происходит в потоке пользовательского интерфейса, то признак продолжения отправляется контексту синхронизации, который выполняет его через цикл сообщений. Так и достигается отзывчивость пользовательского интерфейса.