Поскольку JavaScript является однопоточным, как веб-работники в HTML5 выполняют многопоточность? - PullRequest
52 голосов
/ 14 марта 2012

Я читал о веб-работниках в HTML5, но я знаю, что JavaScript является однопоточным.

Мой вопрос:

Как веб-работники выполняют многопоточную работу?или как они имитируют это, если оно не является действительно многопоточным?Мне здесь не ясно.

Ответы [ 4 ]

60 голосов
/ 14 марта 2012

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

Некоторые моменты, которые могут помочь прояснить ваше мышление:

  • JavaScript - это язык, он не определяет потоковую модель, он не обязательно однопоточный
  • Большинство браузеров исторически были однопоточными (хотя это быстро меняется: IE , Chrome , Firefox ), и большинство реализаций JavaScript происходит в браузерах
  • Веб-работники не являются частью JavaScript, они являются функцией браузера, к которой можно получить доступ через JavaScript
8 голосов
/ 23 ноября 2017

Немного опоздал, но я просто задал себе тот же вопрос и придумал следующий ответ:
Javascript в браузерах всегда однопоточный , и фундаментальным следствием этого является то, что «одновременный» доступ к переменным (основная головная боль многопоточного программирования) на самом деле не параллелен; это верно за исключением веб-работников , которые на самом деле работают в отдельных потоках , и одновременный доступ к переменным должен рассматриваться в несколько явном виде .

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

На самом деле нет необходимости смотреть на WebWorkers, чтобы задействовать параллельную обработку в JavaScript. Подумайте о (асинхронном) AJAX-запросе. И подумайте, как небрежно вы будете обращаться с одновременным доступом к переменным:

var counter = 0;

function asyncAddCounter() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4) {
      counter++;
    }
  };
  xhttp.open("GET", "/a/remote/resource", true);
  xhttp.send();
}

asyncAddCounter();
counter++;

Какое значение counter в конце процесса? Это 2. Неважно, что он читается и пишется «одновременно», это никогда не приведет к 1. Это означает, что доступ к counter всегда согласован. Если два потока действительно обращаются к значению одновременно, они оба могут начать с чтения 0, и оба пишут 1 в конце.

В браузерах фактическая выборка данных удаленного ресурса скрыта для разработчика, и его внутренняя работа выходит за рамки JavaScript API (то, что браузер позволяет вам контролировать с помощью инструкций JavaScript). Что касается разработчика, то результат сетевого запроса обрабатывается основным потоком.
Короче говоря, фактическое выполнение запроса не отображается, но вызов обратного вызова (обработка результата с помощью пользовательского кода JavaScript) выполняется основным потоком.
Возможно, если бы не веб-разработчики, термин «многопоточность» никогда бы не вошел в мир Javascript.

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

Этот факт является причиной, по которой JavaScript называется Управляемым событиями , но не многопоточным.
Обратите внимание, что, таким образом, JavaScript допускает асинхронные идиомы, но не параллельное выполнение кода JavaScript (вне веб-работников). Термин асинхронный просто обозначает тот факт, что результат двух инструкций может быть обработан в зашифрованном порядке.

Что касается WebWorkers, - это API-интерфейсы JavaScript, которые предоставляют разработчику контроль над многопоточным процессом.
Таким образом, они предоставляют явные способы обработки одновременного доступа к совместно используемой памяти (чтение и запись значений в разных потоках), и это делается, среди прочего, следующими способами:

  • вы отправляете данные веб-работнику (что означает, что новый поток читает данные) с помощью структурированного клона: Алгоритм структурированного клона - Web APIs |MDN .По сути, нет «разделяемой» переменной, вместо этого новому потоку дается свежая копия объекта.
  • вы передаете данные веб-работнику, передавая владение значением: Transferable - Web APIs |MDN .Это означает, что только один поток может прочитать его значение в любое время.
  • Что касается результатов, возвращаемых веб-работниками (как они «пишут»), основной поток получает доступ к результатам при появлении соответствующего запроса (например с инструкцией thisWorker.onmessage = function(e) {console.log('Message ' + e.data + ' received from worker');}).Должно быть, это происходит с помощью обычного цикла обработки событий.
  • основной поток и веб-работник получают доступ к действительно общей памяти, SharedArrayBuffer, к которой можно безопасно обращаться по потокам с помощью Atomicфункции.Я обнаружил, что это ясно видно в этой статье: JavaScript: от рабочих к общей памяти
  • примечание: веб-работники не могут получить доступ к DOM, который действительно является общим!
5 голосов
/ 14 марта 2012

Вы создаете файл .js как «рабочий», и он запускает процессы в отдельном потоке. Вы можете передавать данные JSON туда и обратно между ними и «основным» потоком. Работники не имеют доступа к определенным вещам, таким как DOM.

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

0 голосов
/ 14 марта 2012

Браузер пинает поток с помощью JavaScript, который вы хотите выполнить. Так что это реальная нить, с этой вещью веб-работников ваш js больше не является однопоточным.

...