Чем асинхронность отличается от многопоточности
Перейти к содержимому

Чем асинхронность отличается от многопоточности

  • автор:

Параллелизм против многопоточности против асинхронного программирования: разъяснение

В последние время, я выступал на мероприятиях и отвечал на вопрос аудитории между моими выступлениями о Асинхронном программировании, я обнаружил что некоторые люди путали многопоточное и асинхронное программирование, а некоторые говорили, что это одно и тоже. Итак, я решил разъяснить эти термины и добавить еще одно понятие Параллелизм. Здесь есть две концепции и обе они совершенно разные, первая синхронное и асинхронное программирование и вторая – однопоточные и многопоточные приложения. Каждая программная модель (синхронная или асинхронная) может работать в однопоточной и многопоточной среде. Давайте обсудим их подробно.

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

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

image

Здесь мы видим, что мы имеем поток (Поток 1) и 4 задачи, которые необходимо выполнить. Поток начинает выполнять поочередно одну за одной и выполняет их все. (Порядок, в котором задачи выполняются не влияет на общее выполнение, у нас может быть другой алгоритм, который может определять приоритеты задач.

Многопоточность – в этом сценарии, мы использовали много потоков, которые могут брать задачи и приступать к работе с ними. У нас есть пулы потоков (новые потоки также создаются, основываясь на потребности и доступности ресурсов) и множество задач. Итак, поток может работать вот так:

image

Здесь мы можем видеть, что у нас есть 4 потока и столько же задач для выполнения, и каждый поток начинает работать с ними. Это идеальный сценарий, но в обычных условиях мы используем большее количество задач чем количество доступных потоков, таким образом освободившийся поток получает другое задание. Как уже говорилось создание нового потока не происходит каждый раз потому что для этого требуются системные ресурсы такие как процессор, память и начальное количество потоков должно быть определенным.

Теперь давайте поговорим о Асинхронной модели и как она ведет себя в одно и многопоточной среде.

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

image

Здесь мы можем видеть, что один поток отвечает за выполнение всех задач и задачи чередуются друг за другом.

Если наша система способно иметь много потоков тогда все потоки могут работать в асинхронной модели как показано ниже:

image

Здесь мы можем видеть, что одна и та же задача скажем Т4, Т5, Т6 … обрабатывается несколькими потоками. Это красота этого сценария. Как мы можем видеть, что задача Т4 начала выполнение первой Потоком 1 и завершен Потоком 2. Подобным образом задча Т6 выполнена Потоком 2, Потоком 3 и Потоком 4. Это демонстрирует максимальное использование потоков.

Итак, до сих пор мы обсудили 4 сценария:

  • Синхронный однопоточный
  • Синхронный многопоточный
  • Асинхронный однопоточный
  • Асинхронный многопоточный

Параллелизм

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

Как обсуждалось ранее новая эпоха за асинхронным программированием. Почему это так важно?

Преимущества асинхронного программирования

Существует две вещи очень важные для каждого приложения – удобство использования и производительность. Удобство использования потому что пользователь нажав кнопку чтобы сохранить некоторые данные что в свою очередь требует выполнения множества задач таких как чтение и заполнение данных во внутреннем объекте, установление соединения с SQL и сохранения его там. В свою очередь SQL запускается на другой машине в сети и работает под другим процессом, это может потребовать много время. Таким образом если запрос обрабатывается одним процессом экран будет находится в зависшем состоянии до тех пор, пока процесс не завершится. Вот почему сегодня многие приложения и фреймворки полностью полагаются на асинхронную модель.

Производительность приложения и системы также очень важны. Было замечено в то время как выполняется запрос, около 70-80% из них попадают в ожидании зависимых задач. Таким образом, это может быть максимально использовано в асинхронном программирование, где, как только задача передается другому потоку (например, SQL), текущий поток сохраняет состояние и доступен для выполнения другого процесса, а когда задача sql завершается, любой поток, который является свободным, может заняться этой задачей.

Асинхронность в ASP.NET

Асинхронность в ASP.NET может стать большим стимулом для повышения производительности вашего приложения. Вот, как IIS обрабатывает запрос:

image

Когда запрос получен IIS, он берет поток из пула потоков CLR (IIS не имеет какого-либо пула потоков, а сам вместо этого использует пул потоков CLR) и назначает его ему, который далее обрабатывает запрос. Поскольку количество потоков ограничено, и новые могут быть созданы с определенным пределом, тогда если поток будет находится большую часть времени в состоянии ожидания, то это сильно ударит по вашему серверу, вы можете предположить, что это реальность. Но если вы пишете асинхронный код (который теперь становится очень простым и может быть написан почти аналогично синхронному при использовании новых ключевых слов async / await), то он будет работать намного быстрее, и пропускная способность вашего сервера значительно возрастет, потому что вместо ожидания какого-нибудь завершения, он будет доступен пулу потоков, для нового запроса. Если приложение имеет множество зависимостей и длительный процесс выполнения, то для этого приложения асинхронное программирование будет не меньшем благом.

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

Многопоточное vs асинхронное программирование

Хотелось бы узнать разницу между этими подходами. Разве асинхронное программирование не подразумевает из себя уже многопоточность, ведь Task где-то там по любому выполняется в отдельном потоке ?

В каких случаях нужно прибегать к многопоточному, а в каких к асинхронному программированию ?

И еще ко всему этому есть параллельное программирование, которая тоже вносит путаницу для меня. В чем её отличие ?

Murad's user avatar

Попробую собрать воедино все, что дали уже в комментариях.

Есть несколько разных понятий, связанных с областью параллельных вычислений.

  • Конкурентное исполнение (concurrency)
  • Параллельное исполнение (parallel execution)
  • Многопоточное исполнение (multithreading)
  • Асинхронное исполнение (asynchrony)

Каждый из этих терминов строго определен и имеет четкое значение.

Конкурентность (concurrency)

Конкурентность (*) (concurrency) — это наиболее общий термин, который говорит, что одновременно выполняется более одной задачи. Например, вы можете одновременно смотреть телевизор и комментить фоточки в фейсбуке. Винда, даже 95-я могла (**) одновременно играть музыку и показывать фотки.

(*) К сожалению, вменяемого русскоязычного термина я не знаю. Википедия говорит, что concurrent computing — это параллельные вычисления, но как тогда будет parallel computing по русски?

(**) Да, вспоминается анекдот про Билла Гейтса и многозадачность винды, но, теоретически винда могла делать несколько дел одновременно. Хотя и не любых.

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

Конкурентное исполнение говорит о том, что за определенный промежуток времени будет решена более, чем одна задача. Точка.

Параллельное исполнение

Параллельное исполнение (parallel computing) подразумевает наличие более одного вычислительного устройства (например, процессора), которые будут одновременно выполнять несколько задач.

Параллельное исполнение — это строгое подмножество конкурентного исполнения. Это значит, что на компьютере с одним процессором параллельное программирование — невозможно;)

Многопоточность

Многопоточность — это один из способов реализации конкурентного исполнения путем выделения абстракции «рабочего потока» (worker thread).

Потоки «абстрагируют» от пользователя низкоуровневые детали и позволяют выполнять более чем одну работу «параллельно». Операционная система, среда исполнения или библиотека прячет подробности того, будет многопоточное исполнение конкурентным (когда потоков больше чем физических процессоров), или параллельным (когда число потоков меньше или равно числу процессоров и несколько задач физически выполняются одновременно).

Асинхронное исполнение

Асинхронность (asynchrony) подразумевает, что операция может быть выполнена кем-то на стороне: удаленным веб-узлом, сервером или другим устройством за пределами текущего вычислительного устройства.

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

CPU-bound и IO-Bound операции

Еще один важный момент, с точки зрения разработчика — разница между CPU-bound и IO-bound операциями. CPU-Bound операции нагружают вычислительные мощности текущего устройства, а IO-Bound позволяют выполнить задачу вне текущей железки.

Разница важна тем, что число одновременных операций зависит от того, к какой категории они относятся. Вполне нормально запустить параллельно сотни IO-Bound операций, и надеяться, что хватит ресурсов обработать все результаты. Запускать же параллельно слишком большое число CPU-bound операций (больше, чем число вычислительных устройств) бессмысленно.

Возвращаясь к исходному вопросу: нет смысла выполнять в 1000 потоков метод Calc , если он является CPU-Intensive (нагружает центральный процессор), поскольку это приведет к падению общей эффективности вычислений. ОС-ке придется переключать несколько доступных ядер для обслуживания сотен потоков. А этот процесс не является дешевым.

Самым простым и эффективным способом решения CPU-Intensive задачи, заключается в использовании идиомы Fork-Join: задачу (например, входные данные) нужно разбить на определенное число подзадач, которые можно выполнить параллельно. Каждая подзадача должна быть независимой и не обращаться к разделяемым переменным/памяти. Затем, нужно собрать промежуточные результаты и объединить их.

Именно на этом принципе основан PLINQ. О чем можно почитать тут: Джозеф Албахари. Параллельное программирование.

Выглядит это очень интересно:

В этом случае, число потоков будет контролироваться библиотечным кодом в недрах CLR/TPL и метод ComputeMD5 будет вызван параллельно N-раз на компьютере с N-процессорами (ядрами).

Параллелизм, многопоточность, асинхронность: разница и примеры применения (.NET, C#)

Тимур Гайфулин

Многие начинающие специалисты путают многопоточное, асинхронное и параллельное программирование. На первый взгляд, может показаться, что это одно и то же — но нет. Давайте разберёмся, сколько программных моделей используют C#-разработчики и в чём их отличия. Материал подготовлен совместно с Алексеем Гришиным, ведущим разработчиком DD Planet.

Существует несколько концепций: синхронное/асинхронное программирование и однопоточные/многопоточные приложения. Причём первая программная модель может работать в однопоточной или многопоточной среде. То есть приложение может быть: синхронным однопоточным, синхронным многопоточным и асинхронным многопоточным.

Отдельной концепцией считается параллелизм, который является подмножеством многопоточного типа приложений. Рассмотрим особенности каждой программной модели подробнее.

Синхронная модель

Потоку назначается одна задача, и начинается её выполнение. Заняться следующей задачей можно только тогда, когда завершится выполнение первой. Эта модель не предполагает приостановку одной задачи, чтобы выполнить другую.

Однопоточность

Система в одном потоке работает со всеми задачами, выполняя их поочерёдно.

Однопоточная синхронная система

Многопоточность

В этом случае речь о нескольких потоках, в которых выполнение задач идет одновременно и независимо друг от друга.

Иллюстрация: Программные модели

Многопоточная синхронная система

Пример такого концепта — одновременная разработка веб- и мобильного приложений и серверной части, при условии соблюдения архитектурных «контрактов».

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

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

Характеристики асинхронного кода:

  • обрабатывает больше запросов сервера, предоставляя потокам возможность обрабатывать больше запросов во время ожидания результата от запросов ввода-вывода;
  • делает пользовательский интерфейс быстрым, выделяя потоки для обработки действий в пользовательском интерфейсе во время ожидания запросов ввода-вывода, передавая затратные по времени операции другим ядрам ЦП.

Если у системы много потоков, то их асинхронная работа выглядит примерно так:

Многопоточная асинхронная система

Конструкция async/await

Для работы с асинхронными вызовами в C# необходимы два ключевых слова:

  • async — используется в заголовке метода;
  • await — вызывающий метод содержит одно или несколько таких выражений.

Они используются вместе для создания асинхронного метода. У асинхронных методов могут быть следующие типы возвращаемых значений:

  1. Task для асинхронного метода, который выполняет операцию, но не возвращает значение;
  2. Task<TResult> для асинхронного метода, возвращающего значение;
  3. void для обработчика событий;
  4. начиная с версии 7.0 в языке C# поддерживаются любые типы с доступным методом GetAwaiter;
  5. начиная с версии 8.0 в языке C# поддерживается интерфейс IAsyncEnumerable<T> для асинхронного метода, который возвращает асинхронный поток.

Сама конструкция async/await появилась в C# 5.0 с выходом .NET Framework 4.5 и отчасти представляет собой синтаксический сахар. Механизм async/await не имеет реализации в CLR и разворачивается компилятором в сложную конструкцию на IL. Но эта конструкция — не сахар вокруг тасок, а отдельный механизм, использующий класс Task для переноса состояния исполняемой части кода.

Пример асинхронного метода:

Результат асинхронного вычисления факториала

Этот пример приведён лишь для наглядности, особого смысла делать логику вычисления факториала асинхронной нет. Опять же, для имитации долгой работы мы использовали задержку на 8 секунд с помощью методы Thread.Sleep(). Цель была показать: асинхронная задача, которая может выполняться долгое время, не блокирует основной поток — в этом случае метод Main(), и мы можем вводить и обрабатывать данные, продолжая работу с ним.

Параллелизм

Эта программная модель подразумевает, что задача разбивается на несколько независимых подзадач, которые можно выполнить параллельно, а затем объединить результаты. Примером такой задачи может быть Parallel LINQ:

Иллюстрация: Программные модели

Обзор архитектуры параллельного программирования в .NET

Еще один пример — вычисление среднего значения двумерного массива, когда каждый отдельный поток может подсчитать сумму своей строки, а потом объединить результат и вычислить среднее.

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

Какую программную модель выбрать?

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

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

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

Что такое асинхронное программирование? Принцип работы процессора и многопоточность

Что такое асинхронное программирование? Принцип работы процессора и многопоточность

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

Принцип работы процессора

Работа процессора считается тиками — единица времени, в течение которой выполняется одна задача. Чтобы прочитать данные из двух ячеек, просуммировать их, записать получившуюся сумму в последнюю ячейку, необходимо 4 тика. Все сложные операции программирования (умножение, деление) состоят из простых. Умножение заменяется сложением числа с самим собой несколько раз.

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

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

При чтении программы, процессор использует множество процессов. Затем выдаёт конечное действие на экран пользователя. Если процесс проходит линейно, каждый последующий блок кода попадал в блокировку. При скачивании фильма, компьютером нельзя пользоваться, пока 100% файла не загрузятся на жёсткий диск. Чтобы устройство могло выполнять другие функции во время чтения кода, все действия делятся на потоки. Для создания программ, позволяющих выполнять несколько действий одновременно, рассмотрим термины асинхронность и многопоточность.

Что такое асинхронность в программировании?

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

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

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

Что такое многопоточность?

Термин многопоточности схож с асинхронностью. Он связан с принципом построения программного кода, при котором задачи выполняются одновременно. Многопоточность позволяет нескольким потокам работать в рамках одного процесса. При использовании приложения выделяется 1 поток — главный. Он создаётся при запуске компьютера. Главный поток необходим для выполнения следующих функций:

  • обновление интерфейса пользователя;
  • реагирование на действия пользователя;
  • возможность создавать новые потоки;
  • запуск дополнительных блоков с кодом.

Чтобы устройство работало быстрее, главный поток должен быть свободным от задач, которые имеют большое время продолжительности. Если в компьютере находится только 1 поток, он должен выполнять все действия: отрисовка интерфейса, скачивание данных из интернета, просмотр фильмов, принятие запросов из интернета. Нагрузив систему, пользователь не может выполнять другие задачи. Главный поток выполняет только одно действие. После завершения переходит к следующему.

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

Когда необходима асинхронность?

Наиболее распространенная сфера применения асинхронности — использование графическими программами. При работе с ними, основная логика и изменение изображения находятся в разных потоках. Даже когда логика занята, пользователь может совершать действия и выполнять свои задачи.

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

  1. Пользователь отправляет запрос на совершение действия.
  2. Происходит чтение первой части кода.
  3. Остальные блоки с кодом блокируются.
  4. Завершение чтения первой части кода.
  5. Переход к новому блоку.

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

Пример асинхронности

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

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

Добавить комментарий

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