Maison > Article > interface Web > Exemple d'implémentation de la fonction sleep dans nodejs_node.js
La chose la plus désagréable à propos de nodejs est sa nature monothread. Il ne peut pas faire beaucoup de choses et ses performances ne sont pas assez puissantes pour les scénarios gourmands en CPU. Depuis longtemps, je voulais trouver des solutions dans le cadre du langage JavaScript pour résoudre les problèmes d'incapacité à faire fonctionner les threads et de mauvaises performances. La solution qui m'a le plus impressionné était les fibres, mais indépendamment des fibres ou d'autres solutions, le fonctionnement du fil reste très délicat. Il repose trop sur les fils auxiliaires et met la charrue avant les bœufs ; sont concernés, JavaScript est intrinsèquement Le problème de faible performance ne peut pas être résolu ; le plus gênant est que dans le cadre du langage JavaScript, la transmission des messages entre les threads est souvent très limitée et les objets ne peuvent pas être véritablement partagés.
La méthode addon de nodejs est sans aucun doute excellente, avec une flexibilité extrêmement forte, des fonctions complètes et les performances du code natif. En termes simples, cela permet à nodejs d'appeler directement le module c/c, qui est un mode de développement mixte javascript et natif. C'est une bonne chose, pourquoi ne pas l'utiliser ? L'addon devrait être considéré comme un grand sujet. Je ne veux pas en parler trop profondément aujourd'hui, je n'ai pas beaucoup de pratique moi-même. Implémentez ensuite une fonction de veille et considérez-la comme une introduction.
dormir
Pourquoi JavaScript ne peut-il pas implémenter une véritable veille ? La méthode sleep enregistre un signal auprès du noyau du système d'exploitation et envoie un signal de réveil après une heure spécifiée, tandis que le thread lui-même se bloque. Essentiellement, lorsque le thread sleep(1000) signifie dire au système d'exploitation : Ne m'attribuez pas de temps CPU dans les 1000 ms. Par conséquent, la mise en veille peut garantir que le thread ne consomme plus de ressources CPU lorsqu'il est suspendu. JavaScript s'exécute dans un seul thread et annule le concept de thread. Naturellement, il n'existe aucun moyen de suspendre et d'interrompre le thread principal.
Certaines personnes essaieront également d'utiliser javascript pour implémenter le sommeil, par exemple :
Cela utilise une boucle vide pour bloquer l'exécution du processus principal pour implémenter le sommeil, ce qui est évidemment loin du vrai sommeil.
Et si on mettait en place un vrai sommeil ?
Préparation de l'environnement
Environnement de développement
Certains de mes blogs l'ont déjà dit, je vais donc l'omettre ici : node.js npm, python 2.7, visual studio/x-code.
Outils de compilation
L'outil de compilation doit utiliser node-gyp. Les versions plus récentes de nodejs sont fournies avec cette bibliothèque. Si node-gyp ne l'est pas, veuillez exécuter :
.Je n'ai pas l'énergie d'étudier les fonctionnalités de gyp. Si vous connaissez d'autres compilateurs tels que gcc, il n'est pas exclu que gyp ait des incompatibilités, et les options et commutateurs de compilation sont également différents. Il est recommandé de réécrire le code C pour nodejs. S'il existe effectivement des modules qui doivent être réutilisés, vous pouvez envisager d'utiliser le gcc familier pour le compiler dans une bibliothèque de liens dynamiques, puis d'écrire une petite quantité de code pour utiliser le lien dynamique. bibliothèque, puis utilisez gyp pour compiler cette partie du code pour une utilisation par nodejs.
Entrez dans le dossier du projet et exécutez npm init pour initialiser le projet. Afin de faire savoir à nodejs que nous voulons créer un module complémentaire, nous devons ajouter package.json :
Si vous avez utilisé gcc, vous devez vous souvenir du makefile. De même, gyp décrit également la configuration de la compilation via un fichier. Ce fichier est bind.gyp, qui est un fichier json que nous connaissons très bien. gyp n'est pas au centre de notre discussion, donc bind.gyp ne sera pas exploré en profondeur. Nous nous concentrerons uniquement sur les éléments de configuration les plus importants. Voici un exemple de fichier bind.gyp simple mais complet :
Jetez simplement un œil aux trois éléments de configuration impliqués :
1.target_name : Indique le nom du module de sortie.
2.sources : indique le chemin du code source qui doit être compilé. Il s'agit d'un tableau.
3.include_dirs : Indique les répertoires à utiliser lors du processus de compilation. Les fichiers d'en-tête dans ces répertoires peuvent être recherchés dans la directive de précompilation #include. Une manière d'écrire assez particulière est utilisée ici. Au lieu de donner le chemin sous forme de constante de chaîne, nous exécutons une commande node -e "require('nan')". Nous parlerons plus tard de la bibliothèque nan. regardez ce que cette commande produit : node_modulesnan, il s'avère que cette commande signifie renvoyer le chemin de la bibliothèque nan.
Encodage C
OK, maintenant que le code source hello.cc a été configuré, créez un tel fichier. Il y a un problème qui doit être rappelé à l'avance. Le module c que nous écrivons sera finalement utilisé par le moteur v8, donc l'API, la méthode d'écriture, etc. sont limitées par le moteur v8. Différentes versions de nodejs utilisent en fait différentes versions du moteur v8, ce qui signifie qu'il est difficile d'utiliser un ensemble de code C pour satisfaire différentes versions de nodejs (en référence au processus de compilation. Une fois la compilation terminée, il devrait pouvoir être utilisé dans toutes les versions, sans vérification. Oui. Github ne peut pas télécharger de bibliothèques binaires, donc l'open source sur Github provoquera des problèmes. npm peut télécharger directement les bibliothèques binaires et ignorer l'étape de compilation, le problème est donc relativement mineur).
nœud 0.11 et supérieur :
en utilisant l'espace de noms v8 ;
void SleepFunc(const v8::FunctionCallbackInfo
Isoler* isoler = Isolate::GetCurrent();
Portée HandleScope (isoler);
double arg0 = args[0] -> NumberValue();
Sommeil(arg0);
>
void Init (Handle
NODE_MODULE(bonjour, Init);
nœud 0.10 et inférieur :
en utilisant l'espace de noms v8 ;
Poignée
Portée HandleScope ;
double arg0 = args[0] -> NumberValue();
Sommeil(arg0);
Renvoie scope.Close(Undefined());
>
void Init (Handle
NODE_MODULE(bonjour, Init);
On peut voir que les changements sont encore assez importants. Ce serait formidable si ces différences pouvaient être masquées. J'écris tellement juste pour vous dire qu'il existe un moyen. Il est temps d'inviter à sortir avec la bibliothèque Nan.
nan
Rappelez-vous que dans bind.gyp, nous avons introduit le chemin de la bibliothèque nan, qui doit être utilisée ici. A quoi sert la bibliothèque nan ? Il fournit une couche d'abstraction qui protège les différences de syntaxe entre nodejs 0.8, nodejs 0.10, nodejs 0.12 et l'addon avant io.js. louer!
Installez d'abord : npm install --save nan et voyez comment la même fonction est implémentée après avoir utilisé nan :
NAN_METHOD(Sommeil){
NanScope();
Double arg0=args[0]->NumberValue();
Sommeil(arg0);
NanReturnUndefined();
>
void Init (exportations Handle
NODE_MODULE(bonjour, Init);
Ce que vous devez savoir, c'est le set nan. Quant au set v8, vous n'avez pas besoin d'y prêter attention.
En regardant de bas en haut :
Cette phrase définit l'entrée de l'addon. Notez que le premier paramètre doit être cohérent avec notre nom_cible dans bind.gyp. Le deuxième paramètre est la fonction d'entrée de l'addon.
Ce code est la méthode de saisie de l'addon. Il reçoit deux paramètres, à savoir les exportations et le module. L'exemple ci-dessus omet le deuxième paramètre. Si le module fournit un objet, vous pouvez directement spécifier la valeur-clé à fournir aux exports comme celui de l'exemple ; s'il est spécial et ne fournit qu'une valeur ou une fonction, vous devez utiliser le deuxième paramètre, similaire à ; NODE_SET_METHOD(module, "exports", foo); Dans cet exemple, cela signifie sortir un tel module :
Le sommeil est une fonction. Jetons un coup d'œil à la définition du sommeil :
En fait, il s'agit de lire les paramètres passés par javascript, de les convertir en double type, puis d'appeler la méthode sleep de c.
Compiler le module complémentaire
Nous allons maintenant commencer à compiler ce module. Exécutez d’abord node-gyp configure pour préparer la build. Il générera un dossier de build et quelques fichiers. Exécutez ensuite node-gyp build pour démarrer la compilation. Dans cet exemple, un fichier hello.node sera finalement généré dans le répertoire /build/Release/, qui est le module complémentaire qui pourra éventuellement être référencé par javascript.
S'il y a des modifications ultérieures au code c, il n'est pas nécessaire d'exécuter node-gyp configure, exécutez simplement node-gyp build directement.
utilisation de nodejs
Créez un index.js et voyez comment utiliser ce module :
console.log(nouvelle date);
dormir(1000);
console.log(nouvelle date);
// résultat
// Mercredi 4 mars 2015 14:55:18 GMT 0800 (heure standard de Chine)
// Mercredi 4 mars 2015 14:55:19 GMT 0800 (heure standard de Chine)
很容易吧,跟普通的javascript函數的使用方式一模一樣。
至此本文想要分享的技術要點已經闡述完了。不過……究竟跟開篇提供的方法比起來有什麼不一樣?我不截圖了,直接說明結果:
由於addon方式採用的方法是線程掛起,理論上不會有CPU佔用和記憶體變化,結果也是驗證了這一點。再看javascript循環模擬sleep的方式,因為一直在跑循環,記憶體增加一點可以理解,沒什麼大不了;再看CPU佔用25%,似乎還算過得去。真的是這樣嗎?揭露真相的時候到了。我測試的筆記型電腦的CPU是雙核心四線程,再結合25%的CPU佔用…難道雙核心四線程中有一個線程就被這個sleep給佔用了?其實我發現這段期間並沒有一個執行緒被鎖死,不過這不是javascript的功勞,而是intel超線程的功勞。因為說是四線程,其實本質是兩個處理核心只能是雙線程,只是cpu做了一個時間片切割上的小把戲。例如核心cpu01分成了t0和t2,假設在n tick(調度週期)後的一個tick內,任務會分到t0,那麼在再後面一個tick,任務會分到t2。所以從一個比較長的時間尺度(相對於調度週期),一個任務在t0和t2上運行的時間基本上是相當的。於是呈現出來的情境是nodejs的進程沒有佔用t0或t2到100%,而是分別佔了50%上下。由於windows的進程調度相對比較複雜,所以CPU佔用量上下浮動很大。可以這樣預測,如果是雙核心雙執行緒的CPU來處理這個腳本,CPU佔用會上升到50%,而一個核心卡死。如果是單核心CPU來處理,CPU一下子會上升到100%。
好像CPU這段說得有點多,超線程那些也是猜測,各位看看就好。