Всегда ли асинхронный вызов создает / вызывает новый поток? - PullRequest
29 голосов
/ 28 февраля 2009

Всегда ли асинхронный вызов создает новый поток?

Пример:

Если JavaScript однопоточный, то как он может выполнять асинхронную обратную передачу? Это на самом деле блокирует, пока не получит обратный вызов? Если да, то действительно ли это асинхронный вызов?

Ответы [ 6 ]

52 голосов
/ 06 марта 2009

Это интересный вопрос.

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

Вы ссылаетесь на javascript, поэтому давайте обсудим этот язык в среде веб-браузера. Веб-браузер запускает один поток выполнения javascript в каждом окне, он обрабатывает события (такие как onclick = "someFunction ()") и сетевые подключения (такие как вызовы xmlhttprequest).

<script>
function performRequest() {
  xmlhttp.open("GET", "someurl", true);
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4) {
      alert(xmlhttp.responseText);
    }
  }
  xmlhttp.send(sometext);
}
</script>
<span onclick="performRequest()">perform request</span>

(Это нерабочий пример, только для демонстрации концепций).

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

while (true) {
    event = nextEvent(all_event_sources);
    handler = findEventHandler(event);
    handler(event);
}

Важно отметить, что это не «занятый цикл». Это похоже на спящий поток, ожидающий активности. Активность может быть введена пользователем (движение мыши, нажатие кнопки, набор текста) или сетевая активность (ответ от сервера).

Так, в приведенном выше примере,

  1. Когда пользователь нажимает на диапазон, генерируется событие ButtonClicked, findEventHandler () находит событие onclick в теге span, а затем этот обработчик вызывается вместе с событием.
  2. Когда создается запрос xmlhttp, он добавляется в список источников событий all_event_sources.
  3. После того, как функция executeRequest () вернется, основной цикл ожидает на шаге nextEvent (), ожидая ответа. На данный момент ничто не блокирует дальнейшие события от обработки.
  4. Данные возвращаются с удаленного сервера, nextEvent () возвращает сетевое событие, обнаружено, что обработчиком событий является метод onreadystatechange (), этот метод вызывается, и запускается диалоговое окно alert ().

Стоит отметить, что alert () является блокирующим диалогом. Пока этот диалог открыт, дальнейшие события не могут быть обработаны. Эксцентричность модели веб-страниц на языке javascript заключается в том, что у нас есть готовый метод, который блокирует дальнейшее выполнение в контексте этой страницы.

16 голосов
/ 28 февраля 2009

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

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

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

6 голосов
/ 07 марта 2009

Несколько замечаний о JavaScript, в частности:

XMLHttpRequest s не являются блокирующими по умолчанию. Метод send() возвращается сразу после того, как запрос был передан в базовый сетевой стек. Ответ от сервера будет планировать вызов вашего обратного вызова в цикле событий, как описано в других превосходных ответах.

Для этого не требуется новая тема. Базовый API сокетов можно выбрать, аналогично java.nio.channels в Java.

Можно построить синхронные XMLHttpRequest объекты, передав false в качестве третьего параметра open(). Это приведет к тому, что метод send() будет блокироваться до тех пор, пока от сервера не будет получен ответ, что приведет к тому, что цикл событий будет зависеть от задержки в сети и потенциально зависнет от браузера до истечения времени ожидания сети. Это плохая вещь ™.

Firefox 3.5 представит честный многопоточный JavaScript с классом Worker. Фоновый код выполняется в совершенно отдельной среде и связывается с окном браузера, планируя обратные вызовы в цикле событий.

4 голосов
/ 28 февраля 2009

Во многих приложениях с графическим интерфейсом асинхронный вызов (например, invokeLater в Java) просто добавляет объект Runnable в свою очередь потоков GUI. Поток GUI уже создан, и он не создает новый поток. Но потоки даже не требуются строго для асинхронной системы. Возьмите, например, libevent, который использует select / poll / kqueue и т. Д. Для выполнения неблокирующих вызовов сокетов, который затем запускает обратные вызовы в ваш код, полностью без потоков.

2 голосов
/ 09 марта 2009

Я не знаю о javascript, но, например, в мире Windows Forms асинхронные вызовы могут выполняться без нескольких потоков. Это связано с тем, как работает Windows Message Pump. По сути, приложение Windows Forms устанавливает очередь сообщений, через которую Windows помещает сообщения, уведомляющие его о событиях. Например, если вы перемещаете мышь, сообщения будут помещаться в эту очередь. Приложение Windows Forms будет находиться в бесконечном цикле, поглощая все сообщения, которые ему выдаются. В зависимости от того, что содержит каждое сообщение, оно будет перемещать окна, перекрашивать их или даже вызывать пользовательские методы, среди прочего. Вызовы методов определяются делегатами. Когда приложение находит экземпляр делегата в очереди, оно счастливо вызывает метод, указанный делегатом.

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

2 голосов
/ 09 марта 2009

Нет, но будет задействовано более одного потока.

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

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

Итак, подведем итог: будет задействовано более одного потока, но это не обязательно создаст новый поток.

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