Maison > interface Web > js tutoriel > Exemples de méthodes d'utilisation de la programmation multithread dans nodejs_node.js

Exemples de méthodes d'utilisation de la programmation multithread dans nodejs_node.js

WBOY
Libérer: 2016-05-16 16:08:02
original
2316 Les gens l'ont consulté

Dans mon précédent article de blog Ne dites pas que c'est impossible, implémentez sleep dans nodejs , je vous ai présenté l'utilisation de l'addon nodejs. Le thème d'aujourd'hui est toujours un addon, continuez à explorer les capacités de c/c et compensez les faiblesses de nodejs.

J'ai mentionné à plusieurs reprises les problèmes de performances de nodejs. En fait, en ce qui concerne le langage lui-même, les performances de nodejs sont toujours très élevées. Bien qu'elles ne soient pas aussi bonnes que la plupart des langages statiques, l'écart n'est pas grand par rapport aux autres langages dynamiques, et l'avantage en termes de vitesse est très évident ; . Mais pourquoi dit-on souvent que nodejs n’est pas capable de scénarios gourmands en CPU ? Parce qu'en raison de sa nature monothread, il ne peut pas utiliser pleinement le processeur pour les scénarios gourmands en ressources processeur. Il existe une célèbre loi d'Amdahl en informatique :

Supposons que la charge de travail totale W puisse être décomposée en deux parties : Ws qui ne peut être calculée qu'en série et Wp qui permet un calcul parallèle. Ensuite, dans le cas du calcul parallèle de p CPU, les performances peuvent être améliorées par des temps d'accélération. La loi d'Amdahl décrit ce que le parallélisme peut et ne peut pas faire. C’est une situation idéale, la situation réelle sera bien plus complexe. Par exemple, la concurrence est susceptible de provoquer des conflits pour les ressources, nécessitant l'ajout de divers verrous, ce qui laisse souvent le parallélisme dans un état d'attente ; la concurrence entraînera également une surcharge de temps supplémentaire pour le système d'exploitation pour changer de planification de thread, augmentant ainsi les Ws. Cependant, lorsque Wp est beaucoup plus grand que Ws dans une tâche et que plusieurs cœurs de processeur sont disponibles, l’amélioration des performances apportée par le parallélisme est considérable.

D'accord, revenons à nodejs. Imaginons un scénario de calcul : calculez le nombre de nombres premiers compris entre 4 000 000. Lorsque ce scénario est programmé, les opérations de division sont principalement utilisées et les opérations telles que la mémoire et les objets ne sont pas impliquées. En théorie, cela peut garantir que nodejs s'exécute à une vitesse relativement rapide et ne sera pas trop en retard sur c, ce qui est pratique pour. comparaison.

La méthode de recherche des nombres premiers en JavaScript a été fournie dans ce blog, copiez-la directement :

Copier le code Le code est le suivant :

fonction zhishu_js(num) {
Si (num == 1) {
         return false ;
>
Si (num == 2) {
        return true ;
>
pour (var i = 2; i <= Math.sqrt(num); i ) {
Si (num % i == 0) {
              return false ;
>
>
Renvoie vrai ;
>

Écrivez une autre version en langage C :

Copier le code Le code est le suivant :

#include

bool zhishu(int num){
Si (num == 1) {
         return false ;
>
Si (num == 2) {
        return true ;
>
pour (int i = 2; i <= sqrt(num); i ) {
Si (num % i == 0) {
              return false ;
>
>
Renvoie vrai ;
};

Dans nodejs, nous utilisons une boucle de 1 à 4000000 pour récupérer les nombres premiers ; en langage C, nous configurons plusieurs threads et définissons le nombre comme 4000000. Chaque thread fait ce qui suit : si le nombre est supérieur à 0, supprimez-le La valeur de count, calcule s'il s'agit d'un nombre premier et décrémente le compte de 1. Selon cette idée, la version javascript est facile à écrire :

Copier le code Le code est le suivant :

var compte = 0;

pour (j = 1; j < 4000000; j ) {
Si(zhishu(j)){
Compte ;
>
>


La principale difficulté réside dans la programmation multithread en langage C. Au début du c/c, la nécessité du calcul parallèle n’était pas prise en compte, c’est pourquoi la prise en charge du multithread n’était pas fournie dans la bibliothèque standard. Différents systèmes d'exploitation ont généralement des implémentations différentes. Afin d'éviter ce problème, nous utilisons pthread pour gérer les threads.

Téléchargez la dernière version de pthread. Comme je ne suis pas familier avec gyp, il m'a fallu beaucoup de temps pour corriger la dépendance du lien lib. Au final, ma méthode consistait à mettre directement le code source de pthread dans le répertoire du projet, et à ajouter pthread.c au code source. list dans bind.gyp , compilez pthread une fois lors de la compilation du projet. Le fichier bind.gyp modifié ressemble à ceci :

Copier le code Le code est le suivant :

{
"cibles": [
{
"target_name": "bonjour",
"sources": [ "hello.cc","pthreads/pthread.c" ],
"include_dirs": [
" "pthreads"
],
"bibliothèques": ["Ws2_32.lib"]
>
]
>

Bien sûr, ma méthode est très gênante. Si vous ajoutez uniquement des références à la bibliothèque et incluez des répertoires dans pthread, et qu'il n'y a pas de problèmes de dépendance, c'est la meilleure. Il n'est pas nécessaire d'utiliser ma méthode.

Ensuite, entrons dans tout ce qui concerne le multithreading C/C et définissons une fonction de traitement des threads :

Copier le code Le code est le suivant :

pthread_mutex_t lock;

void *thread_p(void *null){
numéro int, x=0;
faire{
          pthread_mutex_lock(&lock);
         num=count--;
         pthread_mutex_unlock(&lock);
Si(num>0){
Si(zhishu(num))x ;
         }autre{
             pause ;
>
}tandis que(vrai);
std::cout<<' '<         renvoie null ;
>

Entre les threads, la variable count est en compétition les unes avec les autres. Nous devons nous assurer qu'un seul thread peut utiliser la variable count en même temps. Nous ajoutons un verrou mutex via pthread_mutex_t lock;. Lorsque pthread_mutex_lock(&lock); est exécuté, le thread vérifie l'état du verrou, s'il est verrouillé, il attend et vérifie à nouveau, bloquant l'exécution du code ultérieur si le verrou est libéré, il verrouille et exécute le code suivant ; En conséquence, pthread_mutex_unlock(&lock); consiste à déverrouiller l'état de verrouillage.

Puisque le compilateur effectue une optimisation de la compilation lors de la compilation, si une instruction ne fait clairement rien et n'a aucun impact sur l'exécution des autres instructions, elle sera optimisée par le compilateur. Dans le code ci-dessus, j'ai ajouté le code pour compter le nombre de nombres premiers. Sinon, cela ressemblerait à ceci :


Copier le code Le code est le suivant :
pour (int j = 0; j < 4000000; j ) {
zhishu(j);
>

sera directement ignoré par le compilateur et ne sera pas réellement exécuté.

La méthode d'écriture pour ajouter un module complémentaire a été introduite. Nous recevons un paramètre de javascript, indiquant le nombre de threads, puis créons un nombre spécifié de threads en c pour terminer la récupération des nombres premiers. Code complet :


Copier le code Le code est le suivant :

#include
#include
#include
#include "pthreadspthread.h"
#définir MAX_THREAD 100
en utilisant l'espace de noms v8 ;

int count=4000000;
pthread_t tid[MAX_THREAD];
pthread_mutex_t lock;

void *thread_p(void *null){
    numéro int, x=0;
    faire{
        pthread_mutex_lock(&lock);
        num=count--;
        pthread_mutex_unlock(&lock);
        si(num>0){
            si(zhishu(num))x ;
        }autre{
            pause;
        >
    }tandis que(vrai);
    std :: cout<<' '≪     pthread_exit(NULL);
    renvoie null ;
>

NAN_METHOD(Zhishu){
    NanScope();
    pthread_mutex_init(&lock,NULL);
    double arg0=args[0]->NumberValue();
    int c=0;
    pour (int j = 0; j < arg0 && j         pthread_create(&tid[j],NULL,thread_p,NULL);
    >
    pour (int j = 0; j < arg0 && j         pthread_join(tid[j],NULL);
    >
    NanReturnUndefined();
>

void Init (exportations Handle){
    exports->Set(NanSymbol("zhishu"), FunctionTemplate::New(Zhishu)->GetFunction());
>

NODE_MODULE(bonjour, Init);

 phread_create est une application joignable, qui peut être rejointe par phread_join.线程join,直到子线程退出。如果子线程已退出,则phread_join不会做任何事。所以对所有的线程都执行thread_join,可以保证所有的线程退出后才会例主线程继续进行。

完善一下nodejs脚本:

复制代码 代码如下 :

var zhishu_c=require('./build/Release/hello.node').zhishu;
fonction zhishu(num) {
    si (num == 1) {
        retourner faux ;
    >
    si (num == 2) {
        renvoie vrai ;
    >
    pour (var i = 2; i <= Math.sqrt(num); i ) {
        si (num % i == 0) {
            retourner faux ;
        >
    >
    renvoie vrai ;
>

console.time("c");
    zhishu_c(100);
console.timeEnd("c");

console.time("js");
var compte=0;
pour (j = 1; j < 4000000; j ) {
    si(zhishu(j)){
        compte ;
    >
>
console.log(count);
console.timeEnd("js");

Jetez un œil aux résultats des tests :

En monothread, bien que la vitesse d'exécution de C/C soit de 181% de celle de nodejs, nous pensons que ce résultat est toujours très bon dans les langages dynamiques. L'amélioration de la vitesse est plus évidente lors de l'utilisation de deux threads. En effet, mon ordinateur dispose d'un processeur double cœur à quatre threads et il est possible que deux cœurs soient utilisés pour le traitement à ce stade. La vitesse atteint le maximum lorsqu'il y a 4 threads. À ce stade, cela devrait être la limite que le dual-core et quatre threads peuvent atteindre. Lorsque le nombre de threads est augmenté, la vitesse ne peut pas être améliorée. Dans la loi d'Amdahl ci-dessus, p a atteint la limite supérieure de 4. L'ajout de threads supplémentaires augmentera le temps de planification des processus du système d'exploitation et le temps de verrouillage, même si cela augmentera également la concurrence pour le temps CPU, dans l'ensemble, l'augmentation des Ws sera plus évidente et les performances diminueront. Si vous faites cette expérience sur une machine inactive, les données devraient être meilleures.

De cette expérience, nous pouvons tirer la conclusion que pour les opérations gourmandes en CPU, l'efficacité sera beaucoup améliorée si elle est laissée aux langages statiques si les calculs impliquent plus de mémoire, de chaînes, de tableaux, de récursivité, etc. (à vérifier plus tard), l'amélioration des performances est encore plus étonnante. Dans le même temps, l'utilisation rationnelle du multi-thread peut améliorer efficacement l'efficacité du traitement, mais plus de threads ne sont pas toujours meilleurs. Ils doivent être configurés de manière appropriée en fonction des conditions de la machine.

Nodejs lui-même n'est en effet pas doué pour gérer les tâches gourmandes en CPU, mais avec l'expérience de cet article, je pense qu'il n'est pas impossible de surmonter cet obstacle.

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal