HTML5 prend en charge les API comme le travailleur Web, permettant aux pages Web d'exécuter du code multithread dans une situation sûre. Cependant, le travailleur Web est en fait soumis à de nombreuses restrictions car il ne peut pas vraiment partager des données de mémoire et ne peut utiliser que des messages pour informer l'état, il ne peut donc même pas être appelé "multi-threading" dans le vrai sens.
L'interface de Web Worker est très gênant à utiliser. Il est essentiellement livré avec un bac à sable, qui exécute un fichier JS indépendant dans le bac à sable, et communique avec le fil principal via PostMessage et OnMessge:
La copie de code est la suivante:
var worker = nouveau travailleur ("my.js");
var bundle = {message: 'Hello world', id: 1};
wearch.postMessage (bundle); // PostMessage peut passer un objet sérialisable dans le passé
wearch.onMessage = function (evt) {
console.log (evt.data); // Comparez les objets remis dans le travailleur avec des objets dans le fil principal
console.log (bundle); // {message: 'Hello World', id: 1}
}
La copie de code est la suivante:
// dans mon.js
onMessage = function (evt) {
var data = evt.data;
data.id ++;
postmessage (données); // {message: 'Hello World', id: 2}
}
Le résultat peut être constaté que l'ID des données obtenues dans le thread a augmenté, mais après son retour, l'ID dans le faisceau du thread principal n'est pas modifié. Par conséquent, l'objet passé dans le thread est réellement copié. De cette façon, le fil ne partage pas les données, en évitant les conflits de lecture et d'écriture, il est donc sûr. Le coût d'assurer la sécurité du fil est de limiter la possibilité de faire fonctionner les objets de thread principaux dans le thread.
Un tel mécanisme limité de lancement multi-lancement est gênant à utiliser. Bien sûr, nous espérons que le travailleur pourra soutenir la possibilité de faire en sorte que le code semble faire fonctionner le multi-threading en même temps. Par exemple, support le code qui ressemble à ce qui suit:
La copie de code est la suivante:
var worker = new ThreadWorker (bundle / * partagé obj * /);
wearch.run (fonction (bundle) {
// Faire STH dans le fil des travailleurs ...
this.runonuithread (fonction (bundle / * partagé obj * /) {
// faire du fil d'interface utilisateur principal ...
});
// ...
});
Dans ce code, après avoir démarré un travailleur, nous pouvons laisser n'importe quel code s'exécuter dans le travailleur, et lorsque nous devons utiliser le thread d'interface utilisateur (tel que la lecture et la rédaction du DOM), nous pouvons revenir au thread principal pour l'exécuter via ce.Runonuithread.
Alors, comment mettre en œuvre ce mécanisme? Regardez le code suivant:
La copie de code est la suivante:
fonction WorkerThread (SharedObj) {
this._worker = nouveau travailleur ("thread.js");
this._complètes = {};
this._task_id = 0;
this.sharedObj = sharedObj;
var self = this;
this._worker.onMessage = fonction (evt) {
var ret = evt.data;
if (ret .__ ui_task __) {
// Exécutez sur une tâche d'interface utilisateur
var fn = (nouvelle fonction ("return" + ret .__ ui_task __)) ();
fn (ret.sharedobj);
}autre{
self.sharedObj = ret.sharedObj;
self._complètes [ret.taskId] (ret);
}
}
}
Workerthread.prototype.run = fonction (tâche, complet) {
var _task = {__thread_task __: task.toString (), sharedObj: this.sharedObj, taskId: this._task_id};
this._complètes [this._task_id ++] = complet;
this._worker.possage (_task);
}
Le code ci-dessus définit un objet ThreadWorker. Cet objet crée un travailleur Web exécutant thread.js, enregistre l'objet partagé SharedObj et traite les messages renvoyés par thread.js.
Si un message UI_TASK est transmis dans thread.js, exécutez la fonction transmise par ce message. Sinon, exécutez le rappel complet de Run, voyons comment Thread.js est écrit:
La copie de code est la suivante:
onMessage = function (evt) {
var data = evt.data;
if (data && data .__ thread_task __) {
var task = data .__ thread_task__;
essayer{
var fn = (nouvelle fonction ("return" + tâche)) ();
var ctx = {
ThreadSignal: vrai,
sommeil: fonction (intervalle) {
ctx.threadSignal = false;
setTimeout (_Run, interval);
},
RunonUithRead: fonction (tâche) {
PostMessage ({__ ui_task __: task.toString (), sharedObj: data.sharedObj});
}
}
fonction _Run () {
ctx.threadSignal = true;
var ret = fn.call (ctx, data.sharedobj);
PostMessage ({error: null, returnvalue: ret, __thread_task __: tâche, sharedObj: data.sharedobj, taskId: data.taskId});
}
_run (0);
} catch (ex) {
PostMessage ({error: ex.ToString (), returnValue: null, sharedObj: data.sharedObj});
}
}
}
On peut voir que Thread.js reçoit des messages des threads d'interface utilisateur, dont le plus important est thread_task, qui est la "tâche" passée par les threads d'interface utilisateur qui doivent être exécutés par le thread de travailleur. Étant donné que la fonction n'est pas sérialisable, il est passé une chaîne. Le thread de travailleur analyse la chaîne dans une fonction pour exécuter les tâches soumises par le thread principal (notez que l'objet partagé partageBj est passé dans la tâche). Une fois l'exécution terminée, le résultat de retour sera transmis au message du fil d'interface utilisateur. Examinons de plus près l'objet partagé SharedObj en plus de la valeur de retour returnValue. Lors du passage, puisque le fil du travailleur et le thread d'interface utilisateur ne partagent pas l'objet, nous synchronisons artificiellement les objets des deux côtés par l'attribution (ce fil est-il sûr? Pourquoi?)
Vous pouvez voir que l'ensemble du processus n'est pas compliqué. Après cette implémentation, ce threadworker peut avoir les deux utilisations suivantes:
La copie de code est la suivante:
var t1 = new WorkerThread ({i: 100} / * partagé obj * /);
setInterval (function () {
t1.run (fonction (sharedObj) {
return sharedObj.i ++;
},
fonction (r) {
console.log ("t1>" + r.returnvalue + ":" + r.error);
}
));
}, 500);
var t2 = new WorkerThread ({i: 50});
t2.run (fonction (sharedObj) {
while (this.threadsignal) {
SharedObj.i ++;
this.runonuithread (fonction (sharedObj) {
W ("body ul"). Appendchild ("<li>" + sharedobj.i + "</li>");
});
this.sleep (500);
}
Retour SharedObj.i;
}, fonction (r) {
Console.log ("T2>" + R.ReturnValue + ":" + R.Error);
});
Cette utilisation donne au code la bonne structure, la flexibilité et la maintenabilité en termes de forme et de sémantique.
D'accord, c'est tout pour la discussion sur l'utilisation du travailleur Web. Les étudiants intéressés peuvent consulter ce projet: https://github.com/akira-cn/workerthread.js (puisque le travailleur doit utiliser des tests de serveur, je mets spécialement un Copycat httpd.js dans le projet, qui est un service HTTP très simple, et vous pouvez l'exécuter directement avec le nœud).