Что такое синхронные и асинхронные вызовы
Перейти к содержимому

Что такое синхронные и асинхронные вызовы

  • автор:

Синхронные и асинхронные операции ввода-вывода

Существует два типа синхронизации ввода-вывода: синхронный ввод-вывод и асинхронный ввод-вывод. Асинхронный ввод-вывод также называется перекрывающимся вводом-выводом.

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

На следующем рисунке показаны два типа синхронизации.

синхронный и асинхронный ввод-вывод

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

Рекомендации по синхронному и асинхронному вводу-выводу

Если файл или устройство открыты для синхронного ввода-вывода (то есть FILE_FLAG_OVERLAPPED не указан), последующие вызовы таких функций, как WriteFile , могут блокировать выполнение вызывающего потока до тех пор, пока не произойдет одно из следующих событий:

  • Операция ввода-вывода завершается (в этом примере — запись данных).
  • Ошибка ввода-вывода. (Например, канал закрывается с другого конца.)
  • Ошибка в самом вызове (например, один или несколько параметров недопустимы).
  • Другой поток в процессе вызывает функцию CancelSynchronousIo , используя дескриптор потока блокировки, который завершает операции ввода-вывода для этого потока, завершая операцию ввода-вывода.
  • Заблокированный поток завершается системой; Например, сам процесс завершается или другой поток вызывает функцию TerminateThread с помощью дескриптора заблокированного потока. (Это, как правило, считается последним и не очень хорошим дизайном приложения.)

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

Процесс открывает файл для асинхронного ввода-вывода в вызове CreateFile , указав флаг FILE_FLAG_OVERLAPPED в параметре dwFlagsAndAttributes . Если FILE_FLAG_OVERLAPPED не указан, файл открывается для синхронного ввода-вывода. При открытии файла для асинхронного ввода-вывода в вызов ReadFile и WriteFile передается указатель на структуру OVERLAPPED. При выполнении синхронного ввода-вывода эта структура не требуется в вызовах ReadFile и WriteFile.

Если файл или устройство открывается для асинхронного ввода-вывода, последующие вызовы функций, таких как WriteFile с использованием этого дескриптора, обычно возвращаются немедленно, но также могут работать синхронно по отношению к заблокированному выполнению. Для получения дополнительной информации см. https://support.microsoft.com/kb/156932.

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

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

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

  • Не отменяйте выделение и не изменяйте структуру OVERLAPPED или буфер данных, пока не будут завершены все асинхронные операции ввода-вывода в объекте файла.
  • Если вы объявляете указатель на структуру OVERLAPPED как локальную переменную, не закрывайте локальную функцию, пока не будут завершены все асинхронные операции ввода-вывода для объекта файла. Если локальная функция завершается преждевременно, структура OVERLAPPED выйдет из область и будет недоступна для всех функций ReadFile или WriteFile, с которыми она сталкивается за пределами этой функции.

Можно также создать событие и поместить дескриптор в структуру OVERLAPPED ; Затем функции ожидания можно использовать для ожидания завершения операции ввода-вывода путем ожидания дескриптора события.

Как было сказано ранее, при работе с асинхронным дескриптором приложения должны проявлять осторожность при определении того, когда следует освобождать ресурсы, связанные с указанной операцией ввода-вывода для этого дескриптора. Если дескриптор освобождается преждевременно, ReadFile или WriteFile могут неправильно сообщить о завершении операции ввода-вывода. Кроме того, функция WriteFile иногда возвращает значение TRUE со значением GetLastErrorERROR_SUCCESS, даже если использует асинхронный дескриптор (который также может возвращать ЗНАЧЕНИЕ FALSE с ERROR_IO_PENDING). Программисты, привыкшие к синхронному проектированию операций ввода-вывода, обычно освобождают ресурсы буфера данных на этом этапе, так как true и ERROR_SUCCESS означают, что операция завершена. Однако если порты завершения ввода-вывода используются с этим асинхронным дескриптором, пакет завершения также будет отправлен, даже если операция ввода-вывода завершена немедленно. Другими словами, если приложение освобождает ресурсы после того, как WriteFile возвращает значение TRUE с ERROR_SUCCESS в дополнение к подпрограмме порта завершения ввода-вывода, оно будет иметь двойное условие ошибки. В этом примере рекомендуется разрешить подпрограмме порта завершения нести полную ответственность за все операции освобождения таких ресурсов.

Система не поддерживает указатель на файл на асинхронных дескрипторах файлов и устройств, поддерживающих указатели на файлы (то есть устройства поиска), поэтому положение файла должно передаваться функциям чтения и записи в связанных членах данных смещения структуры OVERLAPPED . Дополнительные сведения см. в разделах WriteFile и ReadFile.

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

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

Чтобы отменить все ожидающие асинхронные операции ввода-вывода, используйте один из следующих способов:

  • CancelIo — эта функция отменяет только операции, выданные вызывающим потоком для указанного дескриптора файла.
  • CancelIoEx — эта функция отменяет все операции, выполняемые потоками для указанного дескриптора файла.

Используйте CancelSynchronousIo для отмены ожидающих синхронных операций ввода-вывода.

Функции ReadFileEx и WriteFileEx позволяют приложению указать подпрограмму для выполнения (см. FileIOCompletionRoutine) при выполнении асинхронного запроса ввода-вывода.

Синхронные и асинхронные операции

В этом разделе описывается реализация и вызов асинхронных операций службы.

Многие приложения вызывают методы асинхронно, поскольку это позволяет приложению продолжать выполнение других операций, пока осуществляется вызов метода. Службы и клиенты WCF (Windows Communication Foundation) могут участвовать в асинхронных вызовах операций на двух различных уровнях приложения, которые обеспечивают приложениям WCF большую гибкость для увеличения пропускной способности без ущерба для интерактивности.

Типы асинхронных операций

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

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

Независимость контракта службы от реализации службы и клиента делает возможными следующие формы асинхронного выполнения в приложениях WCF:

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

Предлагаемые асинхронные сценарии

Асинхронный подход следует использовать при реализации операции службы, если реализация операции службы осуществляет блокирующий вызов, например операцию ввода-вывода. При реализации асинхронной операции попробуйте вызывать асинхронные операции и методы, чтобы, насколько это возможно, расширить функции асинхронного вызова. Например вызовите метод BeginOperationTwo() из метода BeginOperationOne() .

  • Асинхронный подход следует использовать в клиенте или вызывающем приложении в следующих случаях.
  • Если операции вызываются из приложения промежуточного уровня. (Дополнительные сведения о таких сценариях см. в статье о клиентских приложениях среднего уровня.)
  • Если операции вызываются на страницах ASP.NET, следует использовать асинхронные страницы.
  • Если операции вызываются из любого однопотокового приложения, например приложения Windows Forms или WCF (Windows Presentation Foundation). При использовании модели асинхронных вызовов на основе событий результирующее событие создается в потоке пользовательского интерфейса, в результате чего приложение получает возможность реагирования на действия пользователя, но при этом не требуется управлять несколькими потоками.
  • В общем случае при выборе между синхронным и асинхронным вызовом следует выбирать асинхронный вызов.

Реализация асинхронной операции службы

Асинхронные операции могут быть реализованы с помощью одного из трех следующих методов:

  1. асинхронная модель на основе задач;
  2. асинхронная модель на основе событий;
  3. асинхронная модель IAsyncResult.
Асинхронная модель на основе задач

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

public class SampleService:ISampleService < // . public async TaskSampleMethodTaskAsync(string msg) < return Task.Factory.StartNew(() => < return msg; >); > // . > 

При использовании асинхронной модели на основе задач может быть вызван объект T:System.AggregateException в случае возникновения исключения во время ожидания завершения операции. Это исключение может возникнуть на стороне клиента или службы

Асинхронная модель на основе событий

Служба, поддерживающая асинхронную модель на основе событий, будет содержать одну или несколько операций с именем MethodNameAsync. Эти методы могут копировать синхронные версии, выполняющие ту же операцию в текущем потоке. Этот класс также может содержать событие MethodNameCompleted, а также метод MethodNameAsyncCancel (или просто CancelAsync). Клиент, вызывающий операцию, определяет обработчик событий, вызываемый после завершения операции.

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

public class AsyncExample < // Synchronous methods. public int Method1(string param); public void Method2(double param); // Asynchronous methods. public void Method1Async(string param); public void Method1Async(string param, object userState); public event Method1CompletedEventHandler Method1Completed; public void Method2Async(double param); public void Method2Async(double param, object userState); public event Method2CompletedEventHandler Method2Completed; public void CancelAsync(object userState); public bool IsBusy < get; >// Class implementation not shown. > 

Дополнительные сведения об асинхронной модели на основе событий см. в этой статье.

Асинхронная модель IAsyncResult

Операцию службы можно реализовать в асинхронном режиме, используя платформа .NET Framework асинхронный шаблон программирования и помечая метод свойством AsyncPattern , равным true . В этом случае асинхронная операция доступна в метаданных так же, как и синхронная операция: она предоставляется в виде одной операции с сообщением запроса и согласованным с ним сообщением ответа. В этом случае имеется возможность выбора одной из двух моделей программирования клиента. Этот шаблон может быть представлен в них в виде синхронной или асинхронной операции, поскольку при вызове службы имеет место обмен сообщениями «запрос-ответ».

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

Чтобы определить операцию контракта X , которая выполняется асинхронно независимо от того, как она вызывается в клиентском приложении, выполните следующие действия:

  • Определите два метода, используя шаблон BeginOperation и EndOperation .
  • Метод BeginOperation включает параметры in и ref для операции и возвращает значение типа IAsyncResult.
  • Метод EndOperation включает параметр IAsyncResult, а также параметры out и ref , и возвращает результат возвращаемого типа операции.

См., например, следующий метод.

int DoWork(string data, ref string inout, out string outonly) 
Function DoWork(ByVal data As String, ByRef inout As String, _out outonly As out) As Integer 

Для создания асинхронной операции эти два метода должны иметь следующий вид.

[OperationContract(AsyncPattern=true)] IAsyncResult BeginDoWork(string data, ref string inout, AsyncCallback callback, object state); int EndDoWork(ref string inout, out string outonly, IAsyncResult result); 
 Function BeginDoWork(ByVal data As String, _ ByRef inout As String, _ ByVal callback As AsyncCallback, _ ByVal state As Object) As IAsyncResult Function EndDoWork(ByRef inout As String, ByRef outonly As String, ByVal result As IAsyncResult) As Integer 

Атрибут OperationContractAttribute применяется только к методу BeginDoWork . В получаемом контракте имеется одна операция WSDL с именем DoWork .

Асинхронные вызовы на стороне клиента

Клиентское приложение WCF может использовать любую из трех описанных выше моделей асинхронных вызовов.

При использовании модели на основе задач просто вызовите операцию, используя ключевое слово «await», как показано в следующем фрагменте кода.

await simpleServiceClient.SampleMethodTaskAsync("hello, world"); 

Использование асинхронной модели на основе событий требует добавления обработчика событий для получения уведомлений об ответе. Результирующее событие будет создано в потоке пользовательского интерфейса автоматически. Чтобы воспользоваться этим подходом, укажите параметры командной строки /async и /tcv:Version35 в служебном средстве ServiceModel Metadata Utility Tool (Svcutil.exe), как показано в следующем примере.

svcutil http://localhost:8000/servicemodelsamples/service/mex /async /tcv:Version35 

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

Однако асинхронная модель на основе событий доступна только в платформа .NET Framework 3.5. Кроме того, он не поддерживается даже в платформа .NET Framework 3.5, когда клиентский канал WCF создается с помощью System.ServiceModel.ChannelFactory . Для асинхронного вызова операций в объектах клиентского канала WCF необходимо использовать объекты System.IAsyncResult. Чтобы воспользоваться этим подходом, укажите параметр командной строки /async в служебном средстве ServiceModel Metadata Utility Tool (Svcutil.exe), как показано в следующем примере.

svcutil http://localhost:8000/servicemodelsamples/service/mex /async 

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

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

Шаблоны одностороннего обмена сообщениями

Можно создать асинхронный шаблон обмена сообщениями, в котором односторонние операции (операции, у которых свойство OperationContractAttribute.IsOneWay имеет значение true и нет согласованного ответа) могут отправляться в любом направлении клиентом или службой независимо от противоположной стороны. (Используется дуплексный шаблон обмена сообщениями с односторонними сообщениями.) В этом случае контракт службы определяет односторонний обмен сообщениями, который обе стороны могут реализовать в качестве асинхронных вызовов или реализаций, или нет соответствующим образом. Обычно, если контракт определяет односторонний обмен сообщениями, реализации бывают преимущественно асинхронными, поскольку после отправки сообщения приложение не дожидается ответа и продолжает выполнять другие операции.

Асинхронные клиенты на основе событий и контракты сообщений

Согласно правилам разработки для асинхронной модели на основе событий, если возвращается несколько значений, одно значение возвращается как свойство Result , а остальные возвращаются как свойства объекта EventArgs. Одним из результатов этого является то, что если клиент импортирует метаданные с использованием параметров асинхронных команд на основе событий и операция возвращает более одного значения, объект EventArgs по умолчанию возвращает одно значение как свойство Result , а остальные являются свойствами объекта EventArgs.

Если требуется получать объект сообщения как свойство Result , чтобы возвращаемые значения были свойствами этого объекта, следует использовать параметр командной строки /messageContract. При этом формируется сигнатура, которая возвращает ответное сообщение как свойство Result объекта EventArgs. Все внутренние возвращаемые значения тогда будут свойствами объекта ответного сообщения.

Синхронное и асинхронное программирование: в чем разница?

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

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

Что такое асинхронное программирование?
Асинхронное программирование основано на неблокирующем протоколе ввода-вывода (I/O). Это означает, что асинхронная программа не выполняет операции в иерархическом или последовательном порядке. Получающееся в результате распараллеливание означает, что асинхронная программа может обрабатывать несколько запросов одновременно и независимо.

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

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

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

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

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

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

Масштабируемость
Async/await — это хорошо известная синтаксическая структура многих языков программирования, которая позволяет асинхронным функциям удобно работать с промисами. Промисы — это объекты, которые инкапсулируют желаемое поведение асинхронной операции. Обе эти концепции являются неотъемлемой частью понятия масштабируемости. Масштабируемость может осуществляться двумя способами — горизонтально и вертикально.

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

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

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

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

Совместимость
C++ и JavaScript — самые выдающиеся языки программирования, поддерживающие асинхронное программирование. В этих языках ключевое слово async широко используется и почитается. Но с другими языками дело обстоит не так просто. Хотя, безусловно, можно программировать асинхронные программы практически на любом языке, это будет трудоемкой задачей, если такая реализация не будет предварительно оснащена рассматриваемым языком.

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

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

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

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

Маркетинговый потенциал
Поисковым системам легче сканировать веб-страницы, использующие традиционную синхронную архитектуру. Для маркетологов, которые зависят от поисковой оптимизации (SEO) для создания своей репутации и узнаваемости бренда, это заметное преимущество. Чем больше людей просматривают ваш веб-сайт через Google или Bing, тем больше посетителей будет на вашей веб-странице. Естественно, это положительно скажется на вашем возврате инвестиций (ROI).

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

Скорость
Время загрузки может быть медленнее при синхронном программировании по сравнению с асинхронным программированием. Этого следовало ожидать, учитывая то, как синхронные программы обрабатывают несколько запросов. Когда поток блокируется, другие потоки в очереди также блокируются. Проще говоря, синхронное программирование похоже на посещение Disney World без VIP-пропуска .

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

Когда использовать асинхронное программирование
Самый большой вклад, который обеспечивает асинхронное программирование, — это повышение пропускной способности.

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

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

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

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

Синхронные и асинхронные запросы

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

Синхронный запрос приводит к выполнению кода, который «блокирует» взаимодействие с вкладкой браузера до тех пор, пока запрос не будет полностью выполнен, что существенно ухудшает отклик страницы.

Асинхронные запросы

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

Пример: отправка запроса и получение файла ответа

Приведём простейший пример асинхронного запроса XMLHttpRequest .

var xhr = new XMLHttpRequest(); xhr.open("GET", "/bar/foo.txt", true); xhr.onload = function (e)  if (xhr.readyState === 4)  if (xhr.status === 200)  console.log(xhr.responseText); > else  console.error(xhr.statusText); > > >; xhr.onerror = function (e)  console.error(xhr.statusText); >; xhr.send(null); 

2 строка. 3 параметр метода open установлен как true для того, чтобы явно указать, что этот запрос будет обрабатываться асинхронно.

3 строка. Создаётся функция обработчик события onload . Этот обработчик следить за параметром readyState , для того, чтобы определить завершена ли передача данных и если это так и HTTP статус 200, то полученные данные выводятся в консоль. А если в результате передачи данных возникла ошибка, то сообщение об ошибки будет выведено в консоль.

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

Пример: создание стандартной функции для чтения внешних файлов

В разных сценариях существует необходимость принимать внешние файлы (ответ от сервера, к примеру, json файл). Это стандартная функция, которая использует XMLHttpRequest объект асинхронно, чтобы передать прочитанный контент в специальную функцию обработчик.

function xhrSuccess()  this.callback.apply(this, this.arguments); > function xhrError()  console.error(this.statusText); > function loadFile(url, callback /*, opt_arg1, opt_arg2, . */)  var xhr = new XMLHttpRequest(); xhr.callback = callback; xhr.arguments = Array.prototype.slice.call(arguments, 2); xhr.onload = xhrSuccess; xhr.onerror = xhrError; xhr.open("GET", url, true); xhr.send(null); > 
function showMessage(message)  console.log(message + this.responseText); > loadFile("message.txt", showMessage, "New message!\n\n"); 

Сигнатура вспомогательной функции LoadFile следующая: 1 аргумент — URL адрес для запроса (через HTTP GET), 2 аргумент — функция, которая будет вызвана после успешного выполнения ajax запроса и 3 аргумент — список аргументов, которые будут передаваться через XHR объект в функцию, которая была указана во 2 аргументе.

Строка 1 определяет функцию, которая будет вызвана, когда ajax запрос завершиться успешно. В свою очередь это вызовет функции callback, которая была указана в вызове функции loadFile (то есть функция showMessage ) которая была обозначена как свойство XHR объекта (строка 11). Дополнительные аргументы, которые были указаны при вызове функции loadFile , подставляются в вызов callback функции.

Строка 5 определяет функцию, которая будет вызвана в случаи, если ajax запрос не сможет завершиться успешно.

Строка 11 сохраняет в XHR объекте функцию, которая будет вызвана после успешного завершения ajax запроса. (эта функция передаётся 2 аргументов в вызове функции loadFile ).

12 строка срезает псевдомассив аргументов, который был передан при вызове функции loadFile . Начиная с 3 аргумента все аргументы будут хранится в массиве arguments объекта xhr , который передаётся в функцию xhrSuccess и в конечном итоге будут использованы при вызове функции showMessage , которая будет вызвана функцией xhrSuccess .

Строка 15 устанавливает true для 3 параметра, что явно указывает на то, что запрос будет выполняться асинхронно.

Строка 16 инициализирует запрос.

Пример: использование timeout

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

function loadFile(url, timeout, callback)  var args = Array.prototype.slice.call(arguments, 3); var xhr = new XMLHttpRequest(); xhr.ontimeout = function ()  console.error("The request for " + url + " timed out."); >; xhr.onload = function ()  if (xhr.readyState === 4)  if (xhr.status === 200)  callback.apply(xhr, args); > else  console.error(xhr.statusText); > > >; xhr.open("GET", url, true); xhr.timeout = timeout; xhr.send(null); > 

Отметим, что в код была добавлена функция обработчик события ontimeout .

function showMessage(message)  console.log(message + this.responseText); > loadFile("message.txt", 2000, showMessage, "New message!\n"); 

2 аргумент функции loadFile устанавливает время ожидание равное 2000ms.

Примечание: Поддержка timeout была добавлена начиная с Gecko 12.0.

Synchronous request

Примечание: Starting with Gecko 30.0, Blink 39.0, and Edge 13, synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.

Synchronous XHR often causes hangs on the web. But developers typically don’t notice the problem because the hang only manifests during poor network conditions or slow server response. Synchronous XHR is now in deprecation state. Developers are recommended to move away from the API.

All new XHR features such as timeout or abort aren’t allowed for synchronous XHR. Doing so would invoke InvalidAccessError .

Example: HTTP synchronous request

This example demonstrates how to make a simple synchronous request.

var request = new XMLHttpRequest(); request.open("GET", "/bar/foo.txt", false); // `false` makes the request synchronous request.send(null); if (request.status === 200)  console.log(request.responseText); > 

Line 3 sends the request. The null parameter indicates that no body content is needed for the GET request.

Line 5 checks the status code after the transaction is completed. If the result is 200 — HTTP’s «OK» result — the document’s text content is output to the console.

Example: Synchronous HTTP request from a Worker

One of the few cases in which a synchronous request does not usually block execution is the use of XMLHttpRequest within a Worker .

example.html (the main page):

doctype html> html> head> meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> title>MDN Exampletitle> script type="text/javascript"> var worker = new Worker("myTask.js"); worker.onmessage = function (event)  alert("Worker said: " + event.data); >; worker.postMessage("Hello"); script> head> body>body> html> 

myFile.txt (the target of the synchronous XMLHttpRequest invocation):

Hello World!!

myTask.js (the Worker ):

.onmessage = function (event)  if (event.data === "Hello")  var xhr = new XMLHttpRequest(); xhr.open("GET", "myFile.txt", false); // synchronous request xhr.send(null); self.postMessage(xhr.responseText); > >; 

Примечание: The effect, because of the use of the Worker , is however asynchronous.

It could be useful in order to interact in the background with the server or to preload some content. See Using web workers for examples and details.

Adapting Sync XHR usecases to the Beacon API

There are some cases in which the synchronous usage of XMLHttpRequest was not replaceable, like during the window.onunload and window.onbeforeunload (en-US) events. You should consider using the fetch API with keepalive flag. When fetch with keepalive isn’t available, you can consider using the navigator.sendBeacon API can support these use cases typically while delivering a good UX.

The following example (from the sendBeacon docs) shows a theoretical analytics code that attempts to submit data to a server by using a synchronous XMLHttpRequest in an unload handler. This results in the unloading of the page to be delayed.

.addEventListener("unload", logData, false); function logData()  var client = new XMLHttpRequest(); client.open("POST", "/log", false); // third parameter indicates sync xhr. :( client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8"); client.send(analyticsData); > 

Using the sendBeacon() method, the data will be transmitted asynchronously to the web server when the User Agent has had an opportunity to do so, without delaying the unload or affecting the performance of the next navigation.

The following example shows a theoretical analytics code pattern that submits data to a server by using the sendBeacon() method.

.addEventListener("unload", logData, false); function logData()  navigator.sendBeacon("/log", analyticsData); > 

Смотрите также

  • Использование XMLHttpRequest
  • navigator.sendBeacon

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 3 дек. 2023 г. by MDN contributors.

Your blueprint for a better internet.

  • MDN on Mastodon
  • MDN on X (formerly Twitter)
  • MDN on GitHub
  • MDN Blog RSS Feed

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

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