Что такое асинхронный поток

В Викисловаре есть статья « Асинхронность »
В Викисловаре есть статья « Асинхронизм »

Асинхронность (Асинхронизм) (от греч. α — отрицание, συν — вместе, χρονος — время) — не совпадение с чем-либо во времени; неодномоментность, неодновременность, несинхронность [1] — характеризует процессы, не совпадающие во времени [2] . Термин используемый в специальной литературе, предназначенный для особого применения, в общем смысле, это состояние не являющееся синхронизированным [3] . Та или иная форма используется в зависимости от контекста или исторически сложившихся традиций использования в конкретных областях.

Содержание

Асинхронность в социальном интерактивном взаимодействии [ править | править код ]

  • Асинхронное обучение, личностно-ориентированный метод обучения, который использует онлайн-ресурсы обучения для содействия обмену информацией за пределами ограничений времени и места среди сети людей [4]
  • Совместно редактируемые системы [5]

Асинхронность в схемотехнике [ править | править код ]

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

  • Асинхронная логика, представляет собой последовательную цифровую логическую схему, которая не регулируется схемой синхронизации или общего тактового сигнала
  • Асинхронная система, не имеет глобальных часов: вместо этого, работает под распределенным управлением, с одновременно работающими аппаратными компонентами обмена и синхронизации по каналам
  • Асинхронная коммуникация, передача данных без использования внешнего тактового сигнала, где данные могут быть переданы периодически, а не в стационарном потоке
  • Асинхронная последовательная коммуникация, описывает асинхронный последовательный протокол передачи, в которых сигнал запуска посылается до каждого байта, знака или кодового слова и стоп-сигналом отправляется после каждого кодового слова
  • Асинхронный последовательный интерфейс

Асинхронность в цифровых сетях [ править | править код ]

На более высоком канальном уровне связи, асинхронность является синонимом статистического мультиплексирования. Информация может или не может сразу начать передаваться по требованию отправителя, при этом возможно внесение дополнительной задержки передачи, величина которой меняется в зависимости от загруженности сети. Противоположностью является пример связи с коммутацией каналов, где, как только установится соединение, можно немедленно приступить к передаче данных с гарантированной скоростью и постоянной задержкой.

На практике сеть связи часто синхронна на физическом уровне, и в то же время асинхронна на канальном уровне.

  • Асинхронный режим передачи, в соответствии с определением Форума АТМ (ATM Forum) является «телекоммуникационной концепцией, определенной стандартами ANSI и ITU (ранее CCITT), для передачи полного спектра пользовательского трафика, включая передачу голоса, данных и видео сигналов», и предназначен для унификации телекоммуникационных и компьютерных сетей;
  • Системы Коммутации пакетов, таких как Ethernet или IP.

Асинхронность в информатике [ править | править код ]

В компьютерном программировании, асинхронными событиями являются те, которые возникают независимо от основного потока выполнения программы. Асинхронные действия — действия, выполненные в неблокирующем режиме, что позволяет основному потоку программы продолжить обработку [6] .

  • Событийно-ориентированная архитектура и Событийно-ориентированное программирование требуют асинхронной обработки событий и специального подхода к написанию ПО.
  • Асинхронный клеточный автомат, может обновлять отдельные клетки, независимо, таким образом, что новое состояние клетки влияет на расчет состояний в соседних сотах
  • Асинхронный ввод-вывод, в информатике, является способ осуществления операций ввода/вывода, позволяющий проводить другую обработку, до того как операция ввода/вывода завершена
  • Асинхронный интерфейс прикладного программирования (API)
  • Асинхронный способ доставки (АСД), способ передачи данных, используется когда есть необходимость в стороне сервера обрабатывать большое количество долгосрочными клиентских запросов
  • Ajax, асинхронный JavaScript и XML
  • Асинхронная память

Асинхронность в технике [ править | править код ]

  • В электродвигателях, асинхронный двигатель вариант электродвигателя, где электромагнитное поле вращается с другой (более высокий) скоростью, чем ротор; разница называется скольжением[7]
  • Асинхронизм искрообразования — характеристика регуляторов опережения прерывателя-распределителя зажигания[8] .

Асинхронность в медицине [ править | править код ]

Асинхронизм сердца — один из патогенетических звеньев сердечной недостаточности [9] .

    Статьи, 20 декабря 2018 в 18:04

Традиционно в программировании используют синхронное программирование — последовательное выполнение инструкций с синхронными системными вызовами, которые полностью блокируют поток выполнения, пока системная операция, например чтение с диска, не завершится. В качестве примера ниже написан echo-сервер:

При вызове методов read() и write() текущий поток исполнения будет прерван в ожидании ввода-вывода по сети. Причём большую часть времени программа будет просто ждать. В высоконагруженных системах чаще всего так и происходит — почти всё время программа чего-то ждёт: диска, СУБД, сети, UI, в общем, какого-то внешнего, независимого от самой программы события. В малонагруженных системах это можно решить созданием нового потока для каждого блокирующего действия. Пока один поток спит, другой работает.

Но что делать, когда пользователей очень много? Если создавать на каждого хотя бы один поток, то производительность такого сервера резко упадёт из-за того, что контекст исполнения потока постоянно сменяется. Также на каждый поток создаётся свой контекст исполнения, включая память для стека, которая имеет минимальный размер в 4 КБ. Эту проблему может решить асинхронное программирование.

Асинхронность

Асинхронность в программировании — выполнение процесса в неблокирующем режиме системного вызова, что позволяет потоку программы продолжить обработку. Реализовать асинхронное программирование можно несколькими способами, о которых вы узнаете ниже.

Callbacks

Для написания асинхронной программы можно использовать callback-функции (от англ. callback — обратный вызов) — функции, которые будут вызваны асинхронно каким-либо обработчиком событий после завершения задачи. Переписанный пример сервера на callback-функциях:

В wait_connection() мы всё ещё ждём чего-то, но теперь вместе с этим внутри функции wait_connection() может быть реализовано подобие планировщика ОС, но с callback-функциями (пока мы ждём нового соединения, почему бы не обработать старые? Например, через очередь). Callback-функция вызывается, если в сокете появились новые данные — лямбда в async_read() , либо данные были записаны — лямбда в async_write() .

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

У такого подхода есть несколько проблем. Первую в шутку называют callback hell. Достаточно погуглить картинки на эту тему, чтобы понять, насколько это нечитаемо и некрасиво. В нашем примере всего две вложенные callback-функции, но их может быть намного больше.

Читайте также:  В инстаграмме не публикуется фото

Вторая проблема заключается в том, что код перестал выглядеть как синхронный: появились «прыжки» из wait_connection() в лямбды, например лямбда, переданная в async_write() , что нарушает последовательность кода, из-за чего становится невозможно предсказать, в каком порядке будут вызваны лямбды. Это усложняет чтение и понимание кода.

Async/Await

Попробуем сделать асинхронный код так, чтобы он выглядел как синхронный. Для большего понимания немного поменяем задачу: теперь нам необходимо прочитать данные из СУБД и файла по ключу, переданному по сети, и отправить результат обратно по сети.

Пройдём по программе построчно:

  • Ключевое слово async в заголовке функции говорит компилятору, что функция асинхронная и её нужно компилировать по-другому. Каким именно образом он будет это делать, написано ниже.
  • Первые три строки функции: создание и ожидание соединения.
  • Следующая строка делает асинхронное чтение, не прерывая основной поток исполнения.
  • Следующие две строки делают асинхронный запрос в базу данных и чтение файла. Оператор await приостанавливает текущую функцию, пока не завершится выполнение асинхронной задачи чтения из БД и файла.
  • В последних строках производится асинхронная запись в сокет, но лишь после того, как мы дождёмся асинхронного чтения из БД и файла.

Это быстрее, чем последовательное ожидание сначала БД, затем файла. Во многих реализациях производительность async / await лучше, чем у классических callback-функций, при этом такой код читается как синхронный.

Корутины

Описанный выше механизм называется сопрограммой. Часто можно услышать вариант «корутина» (от англ. coroutine — сопрограмма).

Далее будут описаны различные виды и способы организации сопрограмм.

Несколько точек входа

По сути корутинами называются функции, имеющие несколько точек входа и выхода. У обычных функций есть только одна точка входа и несколько точек выхода. Если вернуться к примеру выше, то первой точкой входа будет сам вызов функции оператором asynс , затем функция прервёт своё выполнение вместо ожидания БД или файла. Все последующие await будут не запускать функцию заново, а продолжать её исполнение в точке предыдущего прерывания. Да, во многих языках в корутине может быть несколько await ’ов.

Для большего понимания рассмотрим код на языке Python:

Программа выведет всю последовательность чисел факториала с номерами от 0 до 41.

Функция async_factorial() вернёт объект-генератор, который можно передать в функцию next() , а она продолжит выполнение корутины до следующего оператора yield с сохранением состояния всех локальных переменных функции. Функция next() возвращает то, что передаёт оператор yield внутри корутины. Таким образом, функция async_factorial() в теории имеет несколько точек входа и выхода.

Stackful и Stackless

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

Так как в корутинах мы можем в любом месте поставить оператор yield , нам необходимо где-то сохранять весь контекст функции, который включает в себя фрейм на стеке (локальные переменные) и прочую метаинформацию. Это можно сделать, например, полной подменой стека, как это делается в stackful корутинах.

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

yield в свою очередь возвращает обратно предыдущий стек-фрейм на исполнение, сохраняя ссылку на конец текущего в предыдущий стек.

Наличие собственного стека позволяет делать yield из вложенных вызовов функций, но такие вызовы сопровождаются полным созданием/сменой контекста исполнения программы, что медленней, чем stackless корутины.

Более производительными, но вместе с тем и более ограниченными, являются stackless корутины. Они не используют стек, и компилятор преобразует функцию, содержащую корутины, в конечный автомат без корутин. Например, код:

Будет преобразован в следующий псевдокод:

По сути здесь создаётся класс, который сохраняет всё состояние функции, а также последнюю точку вызова yield . У такого подхода есть проблема: yield может быть вызван только в теле функции-корутины, но не из вложенных функций.

Симметричные и асимметричные

Корутины также делятся на симметричные и асимметричные.

Симметричные имеют глобальный планировщик корутин, который и выбирает среди всех ожидающих асинхронных операций ту, которую стоит выполнить следующей. Примером является планировщик, о котором говорилось в начале функции wait_connection() .

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

Вывод

Асинхронное программирование является очень мощным инструментом для оптимизации высоконагруженных программ с частым ожиданием системы. Но, как и любую сложную технологию, её нельзя использовать только потому, что она есть. Необходимо всегда задавать себе вопрос: а нужна ли мне эта технология? Какую практическую пользу она мне даст? Иначе разработчики рискуют потратить очень много сил, времени и денег, не получив никакого профита.

C# 8.0 представляет асинхронные потоки, которые моделируют источник потоковых данных, когда можно получить элементы в потоке данных или создать их асинхронно. C# 8.0 introduces async streams, which model a streaming source of data when the elements in the data stream may be retrieved or generated asynchronously. Асинхронные потоки опираются на новые интерфейсы, представленные в .NET Standard 2.1 и реализованные в .NET Core 3.0, чтобы обеспечить естественную модель программирования для асинхронных источников потоковых данных. Async streams rely on new interfaces introduced in .NET Standard 2.1 and implemented in .NET Core 3.0 to provide a natural programming model for asynchronous streaming data sources.

В этом руководстве вы узнаете, как: In this tutorial, you’ll learn how to:

  • Создать источник данных, который формирует последовательность элементов данных асинхронно. Create a data source that generates a sequence of data elements asynchronously.
  • Использовать этот источник данных асинхронно. Consume that data source asynchronously.
  • Распознавать, когда новый интерфейс и источник данных предпочтительнее для более ранних синхронных последовательностей данных. Recognize when the new interface and data source are preferred to earlier synchronous data sequences.

Предварительные требования Prerequisites

Вам нужно настроить свой компьютер для выполнения .NET Core, включая компилятор C# 8.0. You’ll need to set up your machine to run .NET Core, including the C# 8.0 compiler. Компилятор C# 8 доступен, начиная с версии 16.3 Visual Studio 2019 или в пакете SDK .NET Core 3.0. The C# 8 compiler is available starting with Visual Studio 2019 version 16.3 or .NET Core 3.0 SDK.

Читайте также:  Как получить информацию о свойствах объекта

Чтобы вы могли получить доступ к конечной точке GraphQL GitHub, необходимо создать маркер доступа GitHub. You’ll need to create a GitHub access token so that you can access the GitHub GraphQL endpoint. Выберите следующие разрешения для маркеров доступа GitHub. Select the following permissions for your GitHub Access Token:

  • repo:status repo:status
  • public_repo public_repo

Храните маркер доступа в надежном месте, чтобы вы могли использовать его для получения доступа к конечной точке API GitHub. Save the access token in a safe place so you can use it to gain access to the GitHub API endpoint.

Храните свой личный маркер доступа в безопасном месте. Keep your personal access token secure. Любое программное обеспечение с вашим личным маркером доступа может выполнять вызовы API GitHub с помощью ваших прав доступа. Any software with your personal access token could make GitHub API calls using your access rights.

В этом руководстве предполагается, что вы знакомы с C# и .NET, включая Visual Studio или .NET Core CLI. This tutorial assumes you’re familiar with C# and .NET, including either Visual Studio or the .NET Core CLI.

Запуск начального приложения Run the starter application

Вы можете получить код для начального приложения, используемый в этом руководстве в репозитории dotnet/samples в папке csharp/tutorials/AsyncStreams. You can get the code for the starter application used in this tutorial from our dotnet/samples repository in the csharp/tutorials/AsyncStreams folder.

Начальное приложение представляет собой консольное приложение, которое использует интерфейс GraphQL GitHub для получения последних проблем, написанных в репозитории dotnet/docs. The starter application is a console application that uses the GitHub GraphQL interface to retrieve recent issues written in the dotnet/docs repository. Начнем с просмотра следующего кода для метода Main начального приложения. Start by looking at the following code for the starter app Main method:

Вы можете задать переменную среды GitHubKey личному маркеру доступа или заменить последний аргумент в вызове на GenEnvVariable с помощью личного маркера доступа. You can either set a GitHubKey environment variable to your personal access token, or you can replace the last argument in the call to GenEnvVariable with your personal access token. Не помещайте свой код доступа в исходный код, если вы будете сохранять исходный код вместе с другими или помещать его в общий репозиторий с исходным кодом. Don’t put your access code in source code if you’ll be saving the source with others, or putting it in a shared source repository.

После создания клиента GitHub код в Main создает объект отчета о ходе выполнения и маркер отмены. After creating the GitHub client, the code in Main creates a progress reporting object and a cancellation token. После создания этих объектов Main вызывает runPagedQueryAsync , чтобы получить более 250 недавно созданных проблем. Once those objects are created, Main calls runPagedQueryAsync to retrieve the most recent 250 created issues. Результаты отобразятся после выполнения этой задачи. After that task has finished, the results are displayed.

При запуске начального приложения вы можете обнаружить некоторые важные замечания о том, как будет выполняться приложение. When you run the starter application, you can make some important observations about how this application runs. Вы увидите ход выполнения, передаваемый каждой странице, возвращенной с GitHub. You’ll see progress reported for each page returned from GitHub. Прежде чем GitHub вернет каждую новую страницу проблем, возникает заметная пауза. You can observe a noticeable pause before GitHub returns each new page of issues. Наконец, проблемы отображаются только после того, как получены все 10 страниц с GitHub. Finally, the issues are displayed only after all 10 pages have been retrieved from GitHub.

Изучение реализации Examine the implementation

Реализация показывает, почему возникло поведение, обсуждавшееся в предыдущем разделе. The implementation reveals why you observed the behavior discussed in the previous section. Изучите код для runPagedQueryAsync . Examine the code for runPagedQueryAsync :

Давайте сконцентрируемся на алгоритме разбивки по страницам и асинхронной структуре предыдущего кода. Let’s concentrate on the paging algorithm and async structure of the preceding code. (Дополнительные сведения об API GraphQL GitHub см. в этой документации.) Метод runPagedQueryAsync перечисляет проблемы от самых последних до самых старых. (You can consult the GitHub GraphQL documentation for details on the GitHub GraphQL API.) The runPagedQueryAsync method enumerates the issues from most recent to oldest. Чтобы продолжить с предыдущей страницы, он запрашивает по 25 выпусков на страницу и проверяет структуру ответа pageInfo . It requests 25 issues per page and examines the pageInfo structure of the response to continue with the previous page. Это следует за стандартной поддержкой страниц GraphQL для многостраничных ответов. That follows GraphQL’s standard paging support for multi-page responses. Ответ включает в себя объект pageInfo , который содержит значение hasPreviousPages и startCursor , используемые для запроса предыдущей страницы. The response includes a pageInfo object that includes a hasPreviousPages value and a startCursor value used to request the previous page. Проблемы в массиве nodes . The issues are in the nodes array. Метод runPagedQueryAsync добавляет эти узлы в массив, который содержит результаты со всех страниц. The runPagedQueryAsync method appends these nodes to an array that contains all the results from all pages.

После получения и восстановления страницы результатов runPagedQueryAsync сообщает о ходе выполнения и проверяет наличие отмены. After retrieving and restoring a page of results, runPagedQueryAsync reports progress and checks for cancellation. Если есть запрос на отмену, runPagedQueryAsync выдает OperationCanceledException. If cancellation has been requested, runPagedQueryAsync throws an OperationCanceledException.

Существует несколько элементов в этом коде, которые можно улучшить. There are several elements in this code that can be improved. Самое главное, runPagedQueryAsync должен выделить хранилище для всех возвращенных проблем. Most importantly, runPagedQueryAsync must allocate storage for all the issues returned. Этот пример останавливается после нахождения 250 проблем, так как для извлечения всех открытых проблем потребуется гораздо больше памяти на их хранение. This sample stops at 250 issues because retrieving all open issues would require much more memory to store all the retrieved issues. Кроме того, протоколы для поддержки хода выполнения и отмены делают алгоритм более сложным для понимания при первом чтении. In addition, the protocols for supporting progress and supporting cancellation make the algorithm harder to understand on its first reading. Необходимо определить класс, где предоставляется ход выполнения. You must look for the progress class to find where progress is reported. Вы также должны отслеживать передачу данных с помощью CancellationTokenSource и связанный с ним CancellationToken, чтобы понять, где запрашивается отмена и где она предоставляется. You also have to trace the communications through the CancellationTokenSource and its associated CancellationToken to understand where cancellation is requested and where it’s granted.

Читайте также:  Режим дня школьника шаблоны распечатать

Предоставление лучшего способа асинхронных потоков Async streams provide a better way

Асинхронные потоки и связанная языковая поддержка обращаются ко всем этим вопросам. Async streams and the associated language support address all those concerns. Код, который формирует последовательность, теперь может использовать yield return для возврата элементов в методе, который был объявлен с помощью модификатора async . The code that generates the sequence can now use yield return to return elements in a method that was declared with the async modifier. Вы можете применить асинхронный поток, используя цикл await foreach , аналогично любой последовательности с помощью цикла foreach . You can consume an async stream using an await foreach loop just as you consume any sequence using a foreach loop.

Эти новые языковые функции зависят от трех новых интерфейсов, добавленных в .NET Standard 2.1 и реализованных в .NET Core 3.0. These new language features depend on three new interfaces added to .NET Standard 2.1 and implemented in .NET Core 3.0:

Большинство разработчиков C# должны знать об этих трех интерфейсах. These three interfaces should be familiar to most C# developers. Они ведут себя подобно своим синхронным аналогам. They behave in a manner similar to their synchronous counterparts:

Один тип, который может быть незнаком, — System.Threading.Tasks.ValueTask. One type that may be unfamiliar is System.Threading.Tasks.ValueTask. Структура ValueTask предоставляет API, аналогичный классу System.Threading.Tasks.Task. The ValueTask struct provides a similar API to the System.Threading.Tasks.Task class. ValueTask используется в этих интерфейсах по причинам производительности. ValueTask is used in these interfaces for performance reasons.

Преобразование в асинхронные потоки Convert to async streams

Затем для создания асинхронного потока преобразуйте метод runPagedQueryAsync . Next, convert the runPagedQueryAsync method to generate an async stream. Сначала измените подпись runPagedQueryAsync , чтобы вернуть IAsyncEnumerable , затем удалите маркер отмены и объекты хода выполнения из списка параметров, как показано в следующем коде. First, change the signature of runPagedQueryAsync to return an IAsyncEnumerable , and remove the cancellation token and progress objects from the parameter list as shown in the following code:

В следующем коде показано, как начальный код обрабатывает каждую страницу для извлечения. The starter code processes each page as the page is retrieved, as shown in the following code:

Замените эти три строки следующим кодом. Replace those three lines with the following code:

Вы также можете удалить объявление finalResults ранее в этом методе и оператор return , следующий за измененным циклом. You can also remove the declaration of finalResults earlier in this method and the return statement that follows the loop you modified.

Вы завершили изменения для создания асинхронного потока. You’ve finished the changes to generate an async stream. Готовый метод должен напоминать код, указанный ниже. The finished method should resemble the code below:

Затем измените код, который использует коллекцию, для асинхронного потока. Next, you change the code that consumes the collection to consume the async stream. Найдите следующий код в Main , который обрабатывает коллекцию проблем. Find the following code in Main that processes the collection of issues:

Замените код следующим циклом await foreach . Replace that code with the following await foreach loop:

Вы можете получить код для готового руководства, используемый в репозитории dotnet/samples в папке csharp/tutorials/AsyncStreams. You can get the code for the finished tutorial from the dotnet/samples repository in the csharp/tutorials/AsyncStreams folder.

Запуск готового приложения Run the finished application

Снова запустите приложение. Run the application again. Сравните его поведение с поведением начального приложения. Contrast its behavior with the behavior of the starter application. Первая страница результатов перечисляется, как только она становится доступной. The first page of results is enumerated as soon as it’s available. Поскольку каждую новую страницу запрашивают и извлекают, результаты следующей страницы быстро перечисляются, возникает пауза. There’s an observable pause as each new page is requested and retrieved, then the next page’s results are quickly enumerated. Блок try / catch не требует обработки отмены. Вызывающий может прекратить перечисление коллекции. The try / catch block isn’t needed to handle cancellation: the caller can stop enumerating the collection. Отчет о ходе выполнения четко сформирован, так как асинхронный поток формирует результаты скачивания каждой страницы. Progress is clearly reported because the async stream generates results as each page is downloaded. Состояние каждой возвращенной проблемы включается в цикл await foreach . The status for each issue returned is seamlessly included in the await foreach loop. Для отслеживания хода выполнения объект обратного вызова не требуется. You don’t need a callback object to track progress.

Изучив код, вы увидите улучшения в использовании памяти. You can see improvements in memory use by examining the code. Вам больше не нужно выделять коллекцию для хранения всех результатов до их перечисления. You no longer need to allocate a collection to store all the results before they’re enumerated. Вызывающий может определить, как использовать результаты и нужен ли набор хранилищ. The caller can determine how to consume the results and if a storage collection is needed.

Запустите начальное и готовое приложение, и вы увидите различия между реализациями самостоятельно. Run both the starter and finished applications and you can observe the differences between the implementations for yourself. Вы можете удалить маркер доступа GitHub, созданный при начале работы с этим руководством, после завершения изучения. You can delete the GitHub access token you created when you started this tutorial after you’ve finished. Если злоумышленник получил доступ к этому маркеру, ему удастся получить доступ к API GitHub с помощью ваших учетных данных. If an attacker gained access to that token, they could access GitHub APIs using your credentials.

Оставьте ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *