web-dev-qa-db-fra.com

Node.js vs Async / attendent dans .net

Quelqu'un peut-il m'expliquer/me rediriger, quelle est la différence entre le modèle asynchrone de Node.js (thread non bloquant) et tout autre langage, par exemple la façon asynchrone de c # de gérer les E/S. Cela me semble que les deux sont le même modèle. Veuillez suggérer.

50
PKV

Les deux modèles sont très similaires. Il existe deux différences principales, dont l'une disparaîtra bientôt (pour une définition de "bientôt").

Une différence est que Node.js est multithread asynchrone, tandis qu'ASP.NET est multithread asynchrone. Cela signifie que le code Node.js peut faire des hypothèses simplificatrices, car tous votre code s'exécute toujours sur le même thread exact. Ainsi, lorsque votre code ASP.NET awaits, il pourrait éventuellement reprendre sur un thread différent, et c'est à vous d'éviter des choses comme l'état local du thread.

Cependant, cette même différence est également un atout pour ASP.NET, car cela signifie que async ASP.NET peut évoluer dès le départ jusqu'à la pleine capacité de votre serveur. Si vous envisagez, par exemple, une machine à 8 cœurs, ASP.NET peut traiter (les parties synchrones de) 8 demandes simultanément. Si vous placez Node.js sur un serveur gonflé, il est courant d'exécuter 8 instances distinctes de Node.js et d'ajouter quelque chose comme nginx ou un simple équilibreur de charge personnalisé qui gère les demandes de routage pour ce serveur. Cela signifie également que si vous souhaitez que d'autres ressources soient partagées à l'échelle du serveur (par exemple, le cache), vous devrez également les déplacer hors processus.

L'autre différence majeure est en fait une différence de langue, pas de plate-forme. La prise en charge asynchrone de JavaScript est limitée aux rappels et aux promesses, et même si vous utilisez les meilleures bibliothèques, vous vous retrouverez toujours avec du code vraiment maladroit lorsque vous faites quelque chose de non trivial. En revanche, le support async/await en C #/VB vous permet d'écrire du code asynchrone très naturel (et plus important encore, maintenable code asynchrone).

Cependant, la différence de langue disparaît. La prochaine révision de JavaScript introduira des générateurs qui, avec une bibliothèque d'assistance, rendront le code asynchrone dans Node.js aussi naturel qu'aujourd'hui en utilisant async/await. Si vous voulez jouer avec le truc "à venir" maintenant, des générateurs ont été ajoutés dans la V8 3.19, qui a été intégrée dans Node.js 0.11.2 (la branche Unstable). Passer --harmony ou --harmony-generators pour activer explicitement le support du générateur.

78
Stephen Cleary

La différence entre le modèle asynchrone de Node.js et les C # asynchrone/attendre le modèle est énorme. Le modèle asynchrone qui a Node.js est similaire au vieux modèle asynchrone en C # et .Net appelé modèle asynchrone basé sur les événements (EAP). C # et .Net ont 3 modèles asynchrones, vous pouvez les lire sur Asynchronous Programming Patterns . Le modèle asynchrone le plus moderne en C # est basé sur les tâches avec C # async et attendre mots-clés, vous pouvez en lire plus sur Pattern asynchrone basé sur les tâches . Les C # async/attendre les mots-clés rendent le code asynchrone linéaire et vous permettent d'éviter le "Callback Hell" bien mieux que dans n'importe quel autre langage de programmation. Vous avez juste besoin de l'essayer, et après cela, vous ne le ferez plus autrement. Vous écrivez simplement des opérations asynchrones consommatrices de code et ne vous inquiétez pas de la lisibilité, car il semble que vous écriviez tout autre code. S'il vous plaît, regardez ces vidéos:

  1. plongée profonde de programmation asynchrone
  2. Async dans ASP.NET
  3. Comprendre les tâches asynchrones et en attente

Et s'il vous plaît, essayez de faire quelque chose d'asynchrone dans C # puis dans Node.js pour comparer. Vous verrez la différence.

ÉDITER: Étant donné que le moteur JavaScript Node.js V8 prend en charge les générateurs, défini dans ECMAScript 6 Draft , "Callback Hell" dans le code JavaScript peut également être facilement évité. Il donne vie à une forme d'async/wait en JavaScript

16
Ashot Muradian

Avec nodejs, toutes les demandes vont dans la file d'attente des événements. La boucle d'événements du nœud utilise un seul thread pour traiter les éléments de la file d'attente d'événements, effectuant tous les travaux non liés aux E/S et envoyant au pool de threads C++ (à l'aide de rappels javascript pour gérer l'asynchronie) tout le travail lié aux E/S. Les threads C++ ajoutent ensuite à la file d'attente d'événements ses résultats.

Les différences avec ASP.NET (les deux premières s'appliquent à peu près à tous les serveurs Web qui autorisent les E/S asynchrones) sont les suivantes:

  1. ASP.NET utilise un thread différent pour chaque demande entrante , vous obtenez donc une surcharge de changement de contexte
  2. .NET ne vous oblige pas à utiliser async pour effectuer un travail lié aux E/S, il n'est donc pas aussi idiomatique que nodejs où les appels d'api liés aux E/S sont de facto asynchrones (avec rappels)
  3. .NET '"wait-async" ajoute une étape au moment de la compilation pour ajouter des "rappels", afin que vous puissiez écrire du code linéaire (aucune fonction de rappel ne passant), contrairement à nodejs

Il y a tellement d'endroits sur le Web qui décrivent l'architecture du nœud, mais voici quelque chose: http://johanndutoit.net/presentations/2013/02/gdg-capetown-nodejs-workshop-23-feb-2013/index .html # 1

6
billy

La différence entre async dans Nodejs et .NET réside dans l'utilisation du multitâche préemptif pour le code utilisateur. .NET utilise le multitâche préemptif pour le code utilisateur, contrairement à Nodejs.

Nodejs utilise un pool de threads interne pour servir les requêtes IO, et un thread unique pour exécuter votre code JS, y compris les callbacks IO.

L'une des conséquences de l'utilisation du multitâche préemptif (.NET) est qu'un état partagé peut être modifié par une autre pile d'exécution lors de l'exécution d'une pile. Ce n'est pas le cas dans Nodejs - aucun rappel d'une opération asynchrone ne peut s'exécuter simultanément avec la pile en cours d'exécution. D'autres piles d'exécution n'existent tout simplement pas en Javascript. Le résultat d'une opération asynchrone serait disponible pour les rappels uniquement lorsque la pile actuelle d'exécution se termine complètement. Ayant cela, simple while(true); bloque Nodejs, car dans ce cas, la pile actuelle ne se ferme pas et la boucle suivante n'est jamais lancée.

Pour comprendre la différence, considérons les deux exemples, un pour js et un pour net. var p = new Promise (fonction (résoudre) {setTimeout (résoudre, 500, "mon contenu");}); p.then (fonction (valeur) {// ... valeur === "mon contenu"

Dans ce code, vous pouvez placer un gestionnaire en toute sécurité (ensuite) après avoir "démarré" une opération asynchrone, car vous pouvez être sûr qu'aucun code de rappel lancé par une opération asynchrone ne s'exécutera jusqu'à ce que la pile d'appels en cours se termine. Les rappels sont traités dans les cycles suivants. Quant aux rappels de temporisation, ils sont traités de la même manière. L'événement de temporisation asynchrone place le traitement de rappel dans la file d'attente pour être traité dans un cycle suivant.

Dans .NET, c'est différent. Il n'y a pas de cycles. Il existe un multitâche préemptif.

ThreadPool.QueueUserWorkItem((o)=>{eventSource.Fire();});
eventSource.Fired += ()=>{
 // the following line might never execute, because a parallel execution stack in a thread pool could have already been finished by the time the callback added.
 Console.WriteLine("1");
}

Voici un code Hello World .NET a-la Nodejs pour illustrer le traitement asynchrone sur un seul thread et l'utilisation d'un pool de threads pour les E/S asynchrones, tout comme le fait le nœud. (.NET inclut les versions TPL et IAsyncResult des opérations async IO, mais il n'y a aucune différence dans le cadre de cet exemple. Quoi qu'il en soit, tout se termine avec des threads différents sur un pool de threads.)

void Main()
{
    // Initializing the test
    var filePath = Path.GetTempFileName();
    var filePath2 = Path.GetTempFileName();
    File.WriteAllText(filePath, "World");
    File.WriteAllText(filePath2, "Antipodes");

    // Simulate nodejs
    var loop = new Loop();

    // Initial method code, similar to server.js in Nodejs. 
    var fs = new FileSystem();

    fs.ReadTextFile(loop, filePath, contents=>{
        fs.WriteTextFile(loop, filePath, string.Format("Hello, {0}!", contents),
            ()=>fs.ReadTextFile(loop,filePath,Console.WriteLine));
    });

    fs.ReadTextFile(loop, filePath2, contents=>{
        fs.WriteTextFile(loop, filePath2, string.Format("Hello, {0}!", contents),
            ()=>fs.ReadTextFile(loop,filePath2,Console.WriteLine));
    });

    // The first javascript-ish cycle have finished.

    // End of a-la nodejs code, but execution have just started.

    // First IO operations could have finished already, but not processed by callbacks yet

    // Process callbacks
    loop.Process();

    // Cleanup test
    File.Delete(filePath);
    File.Delete(filePath2);
}

public class FileSystem
{
    public void ReadTextFile(Loop loop, string fileName, Action<string> callback)
    {
        loop.RegisterOperation();
        // simulate async operation with a blocking call on another thread for demo purposes only.
        ThreadPool.QueueUserWorkItem(o=>{
            Thread.Sleep(new Random().Next(1,100)); // simulate long read time

            var contents = File.ReadAllText(fileName);
            loop.MakeCallback(()=>{callback(contents);});
        });
    }

    public void WriteTextFile(Loop loop, string fileName, string contents, Action callback)
    {
        loop.RegisterOperation();
        // simulate async operation with a blocking call on another thread for demo purposes only.
        ThreadPool.QueueUserWorkItem(o=>{
            Thread.Sleep(new Random().Next(1,100)); // simulate long write time

            File.WriteAllText(fileName, contents);
            loop.MakeCallback(()=>{callback();});
        });
    }
}

public class Loop
{
    public void RegisterOperation()
    {
        Interlocked.Increment(ref Count);
    }
    public void MakeCallback(Action clientAction)
    {
        lock(sync)
        {
            ActionQueue.Enqueue(()=>{clientAction(); Interlocked.Decrement(ref Count);});
        }
    }

    public void Process()
    {
        while(Count > 0)
        {
            Action action = null;
            lock(sync)
            {
                if(ActionQueue.Count > 0)
                {
                    action = ActionQueue.Dequeue();
                }
            }

            if( action!= null )
            {
                action();
            }
            else
            {
                Thread.Sleep(10); // simple way to relax a little bit.
            }
        }
    }

    private object sync = new object();

    private Int32 Count;

    private Queue<Action> ActionQueue = new Queue<Action>();
}
2
George Polevoy