HTML5 поддерживает API, такие как Web Worker, позволяя веб-страницам выполнять многопоточный код в безопасной ситуации. Тем не менее, веб-работник на самом деле подчиняется многим ограничениям, потому что он не может по-настоящему обмениваться данными памяти и может использовать только сообщения для информирования о состоянии, поэтому его даже нельзя назвать «мультипоточным» в истинном смысле.
Интерфейс веб -работника очень неудобен в использовании. Он в основном поставляется с песочницей, которая запускает независимый файл JS в песочнице, и общается с основным потоком через Postmessage и Onmessge:
Кода -копия выглядит следующим образом:
var Worker = новый работник ("my.js");
var bundle = {Сообщение: 'Hello World', id: 1};
Worker.postmessage (пакет); // Postmessage может пройти сериализуемый объект в прошлом
Worker.onmessage = function (evt) {
console.log (evt.data); // Сравните объекты, передаваемые обратно в работнике с объектами в основном потоке
Console.log (Bundle); // {Сообщение: 'Hello World', id: 1}
}
Кода -копия выглядит следующим образом:
// в my.js
onmessage = function (evt) {
var data = evt.data;
data.id ++;
postmessage (данные); // {Сообщение: 'Привет, мир', id: 2}
}
Результат можно обнаружить, что идентификатор данных, полученных в потоке, увеличился, но после его передачи обратно, идентификатор в пакете основного потока не изменяется. Следовательно, объект, проходящий в потоке, фактически скопирован. Таким образом, поток не разделяет данные, избегая конфликтов чтения и записи, поэтому это безопасно. Стоимость обеспечения безопасности потока состоит в том, чтобы ограничить возможность управления основными объектами потока в потоке.
Такой ограниченный механизм с многопоточным чтением неудобен для использования. Конечно, мы надеемся, что работник сможет поддержать возможность сделать код, который, похоже, одновременно управлял многопоточным. Например, код поддержки, который выглядит следующим образом:
Кода -копия выглядит следующим образом:
var Worker = new Threadworker (Bundle /*Shared obj* /);
Worker.run (function (bundle) {
// DO STH в рабочей ветке ...
this.runonuithread (function (bundle /*shared obj* /) {
// DO STH в основной потоке пользовательского интерфейса ...
});
// ...
});
В этом коде, после того, как мы запустим работника, мы можем позволить любому коду, запускающемуся в работнике, и когда нам нужно использовать поток пользовательского интерфейса (например, чтение и написание DOM), мы можем вернуться в основной поток, чтобы выполнить это через это.runonuitHread.
Так как реализовать этот механизм? Посмотрите на следующий код:
Кода -копия выглядит следующим образом:
Функциональный работник (sharedobj) {
this._worker = new Worker ("Thread.js");
this._completes = {};
this._task_id = 0;
this.sharedobj = sharedobj;
var self = это;
this._worker.onmessage = function (evt) {
var ret = evt.data;
if (ret .__ ui_task __) {
// запустить задачу пользовательского интерфейса
var fn = (новая функция ("return"+ret .__ ui_task __)) ();
fn (ret.sharedobj);
}еще{
self.sharedobj = ret.sharedobj;
self._completes [ret.taskid] (ret);
}
}
}
Workerthread.prototype.run = function (задача, полная) {
var _task = {__thread_task __: task.tostring (), sharedobj: this.sharedobj, taskid: this._task_id};
this._completes [this._task_id ++] =
this._worker.postmessage (_task);
}
Приведенный выше код определяет объект потока. Этот объект создает веб -работник, использующий Thread.js, сохраняет общий объект SharedObj и обрабатывает сообщения, отправляемые обратно Thread.js.
Если сообщение UI_TASK передается обратно в Thread.js, запустите функцию, передаваемую этим сообщением. В противном случае выполните полный обратный вызов запуска, давайте посмотрим, как написано Thread.js:
Кода -копия выглядит следующим образом:
onmessage = function (evt) {
var data = evt.data;
if (data && data .__ thread_task __) {
var task = data .__ thread_task__;
пытаться{
var fn = (new Function ("return"+task)) ();
var ctx = {
Threadsignal: True,
сон: функция (интервал) {
ctx.threadsignal = false;
setTimeout (_run, интервал);
},
runonuithread: function (задача) {
postmessage ({__ ui_task __: task.tostring (), sharedobj: data.sharedobj});
}
}
функция _run () {
ctx.threadsignal = true;
var ret = fn.call (ctx, data.sharedobj);
postmessage ({ошибка: null, returnvalue: ret, __thread_task __: задача, sharedobj: data.sharedobj, taskid: data.taskid});
}
_run (0);
} catch (ex) {
postmessage ({error: ex.toString (), returnValue: null, sharedobj: data.sharedobj});
}
}
}
Можно видеть, что Thread.js получает сообщения из потоков пользовательского интерфейса, наиболее важным из которых является Thread_task, которая является «задачей», передаваемой по потокам пользовательского интерфейса, которые необходимо выполнять потоком работника. Поскольку функция не является сериализуемой, она передается строкой. Поток работника анализирует строку в функцию для выполнения задач, представленных основным потоком (обратите внимание, что общий объект SharedObj передается в задаче). После завершения выполнения результат возврата будет передаваться в поток пользовательского интерфейса через сообщение. Давайте более подробно рассмотрим общий объект SharedObj в дополнение к возврату returnValue. При отчете обратно, поскольку поток работника и поток пользовательского интерфейса не делятся объектом, мы искусственно синхронизируем объекты с обеих сторон посредством назначения (этот поток безопасен? Почему?)
Вы можете видеть, что весь процесс не сложный. После этой реализации этот работник потока может иметь следующие два использования:
Кода -копия выглядит следующим образом:
var t1 = new Workerthread ({i: 100} /*Общий obj* /);
setInterval (function () {
t1.run (function (sharedobj) {
вернуть sharedobj.i ++;
},
function (r) {
console.log ("t1>" + r.returnvalue + ":" + r.error);
}
);
}, 500);
var t2 = new Workerthread ({i: 50});
t2.run (function (sharedobj) {
while (this.threadsignal) {
sharedobj.i ++;
this.runonuithread (function (sharedobj) {
W ("body ul"). Appendchild ("<li>"+sharedobj.i+"</li>");
});
это. Sleep (500);
}
вернуть sharedobj.i;
}, function (r) {
console.log ("t2>" + r.returnvalue + ":" + r.error);
});
Это использование дает коду хорошую структуру, гибкость и обслуживаемость как в форме, так и в семантических терминах.
Хорошо, это все для обсуждения использования веб -работника. Заинтересованные студенты могут взглянуть на этот проект: https://github.com/akira-cn/workerthread.js (поскольку работник должен использовать серверное тестирование, я специально помещаю в проект очень простой HTTP-сервис JS, и вы можете запустить его с помощью Node).