2009-02-28 8 views
26

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

Пример:

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

ответ

50

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

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

Вы ссылаетесь на 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. Когда пользователь нажимает на пролете, A ButtonClicked событие будет генерироваться, findEventHandler() найдет событие OnClick на тег диапазона, а затем этот обработчик будет вызывается с событием.
  2. Когда запрос xmlhttp создается, он добавляется в список источников событий all_event_sources.
  3. После того, как функция executeRequest() вернется, mainloop ждет следующего шага nextEvent(), ожидая ответа. На данный момент нет никаких «блокировок» дальнейших событий от обработки.
  4. Данные возвращаются с удаленного сервера, nextEvent() возвращает сетевое событие, обработчик события является методом onreadystatechange(), этот метод вызывается и запускается диалог alert().

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

+0

предложите отредактировать ответ «Стоит ничего» отличается от «Стоит noTing» , который, я думаю, является вашим намерением. Чтобы избежать путаницы для читателей – MadMurf

+0

@MadMurf: Я тоже это заметил ... и исправил. – mpen

+0

>> Асинхронное программирование - это парадигма программирования, которая в основном однопоточная << - Не пытайтесь быть педантичной здесь, но я не вижу, как это утверждение верно. Асинхронное программирование подразумевает что-либо о потоках, не говоря уже о одиночном и множественном? – Lee

15

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

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

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

+1

>> Асинхронный вызов не является новой нитью << - Я думаю, что это вводящее в заблуждение утверждение. Я не знаю ничего, что мешает JS обслуживать XHR-запрос новыми потоками. Я думаю, вы имеете в виду, что новый поток не гарантируется или не предлагается спецификацией, но определенно может быть так, как он реализуется системой. – Lee

4

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

5

Пара заметка о JavaScript в частности:

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

Это не требует новой нити. Исходный сокет API можно выбрать, аналогично java.nio.channels на Java.

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

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

+0

, когда вы сказали, что «Ответ от сервера будет планировать вызов вашего обратного вызова в цикле событий», который/какой поток помещает событие в цикле событий, чтобы вызвать обратный вызов для вызова? – Yiling

+1

@Yiling Событие создается потоком ввода-вывода, внутренним для браузера, а не потоком, выполняющим JavaScript. – Matthew

2

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

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

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

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

2

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

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

Смежные вопросы