57 votes

Un serveur Web asynchrone à un seul thread, non bloquant (comme Node.js) est-il possible dans .NET?

J'ai été confronté à cette question, vous cherchez un moyen de créer un thread unique, basé sur des événements non bloquantes asynchrone serveur web .NET.

Cette réponse avait l'air prometteur au premier abord, en affirmant que le corps du code s'exécute dans un thread unique.

Cependant, j'ai testé cela en C#:

using System;
using System.IO;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

        var sc = new SynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(sc);
        {
            var path = Environment.ExpandEnvironmentVariables(
                @"%SystemRoot%\Notepad.exe");
            var fs = new FileStream(path, FileMode.Open,
                FileAccess.Read, FileShare.ReadWrite, 1024 * 4, true);
            var bytes = new byte[1024];
            fs.BeginRead(bytes, 0, bytes.Length, ar =>
            {
                sc.Post(dummy =>
                {
                    var res = fs.EndRead(ar);

                    // Are we in the same thread?
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                }, null);
            }, null);
        }
        Thread.Sleep(100);
    }
}

Et le résultat a été:

1
5

Il semble donc que, contrairement à la réponse, le fil de l'initiation de la lecture et le fil fin de la lecture sont pas les mêmes.

Alors maintenant ma question est, comment faites-vous pour réaliser un thread unique, basé sur des événements non bloquantes asynchrone serveur web .NET?

40voto

Sam Saffron Points 56236

L'ensemble de l' SetSynchronizationContext est un leurre, c'est juste un mécanisme de regroupement, le travail se produit toujours dans les IO de Pool de Threads.

Ce que vous demandez est un moyen de file d'attente et de la récolte, les Appels de Procédure Asynchrone pour tous vos IO travail depuis le thread principal. De nombreux niveau supérieur des cadres de l'envelopper de ce genre de fonctionnalité, le plus célèbre étant libevent.

Il y a un grand récapitulatif sur les différentes options ici: quelle est la différence entre epoll, sondage, threadpool?.

.NET se charge déjà de mise à l'échelle pour vous en avoir un spécial "IO Pool de Threads" qui gère IO accès lorsque vous appelez l' BeginXYZ méthodes. Cette IO Pool de Threads doit avoir au moins 1 thread par le processeur sur la boîte. voir: pool de threads.SetMaxThreads.

Si seul thread app est une exigence critique de quelques fous de la raison), vous pouvez, bien sûr, d'interopérabilité de toutes ces choses à l'aide de DllImport (voir un exemple ici)

Cependant, il serait très complexe et risqué de la tâche:

Pourquoi n'avons-nous pas le soutien de l'Apc comme un mécanisme d'auto-complétion? Les apc sont vraiment pas un bon usage général mécanisme d'auto-complétion de code d'utilisateur. La gestion de la réentrée introduit par l'Apc est presque impossible; tout moment vous bloquer sur un verrou, par exemple, certains arbitraire fin d'e/S peut prendre le contrôle de votre fil. Il pourrait essayer d'acquérir les verrous de sa propre, ce qui peut introduire de verrouillage de la commande de problèmes et donc de blocage. La prévention ce qui nécessite une conception soignée, et la capacité de faire en sorte que quelqu'un d'autre code ne sera jamais exécuté, au cours de votre alertable attendre, et vice-versa. Cela limite grandement l'utilité de l'Apc.

Donc, pour récapituler. Si vous voulez un seul thread processus de gestion qui fait tout son travail à l'aide de l'APC et de l'achèvement des ports, vous allez avoir à la main code. D'un bâtiment, il serait risqué et difficile.

Si vous voulez tout simplement à grande échelle de réseaux, vous pouvez continuer à l'utiliser BeginXYZ et de la famille et de repos assuré qu'il sera bien, car il utilise de l'APC. Vous payez un mineur prix ordonnancement des choses entre les threads et les .NET notamment la mise en œuvre.

De: http://msdn.microsoft.com/en-us/magazine/cc300760.aspx

La prochaine étape dans l'expansion le serveur est d'utiliser asynchronous I/o I/O Asynchrone atténue la nécessité de créer et de gérer des threads. Cela conduit à un code bien plus simple et aussi plus efficace pour I/O modèle. Asynchronous I/O utilise des fonctions pour gérer les données et les connexions, ce qui signifie qu'il n'existe pas de listes de configurer et d'analyse, et il n'est pas nécessaire de créer de nouveaux threads de travail pour faire face à l'attente de l'I/O.

Intéressant, côté de fait, c'est que seul thread n'est pas le moyen le plus rapide pour faire async sockets sous Windows à l'aide d'achèvement des ports, voir: http://doc.sch130.nsc.ru/www.sysinternals.com/ntw2k/info/comport.shtml

Le but du serveur est de subir quelques changements de contexte que possible en demandant à son fils éviter les blocages inutiles, alors que dans le même temps maximiser le parallélisme par l'utilisation de plusieurs threads. L'idéal est d'avoir un thread activement à l'entretien d'une demande du client sur chaque processeur et pour ces fils de ne pas bloquer si il y a d'autres demandes en attente lorsqu'ils ont terminé leur demande. Pour que cela fonctionne correctement, cependant, il doit y avoir un moyen de l'application pour activer un autre thread lorsqu'un traitement d'une demande du client, des blocs d'e/S (comme quand on lit un fichier dans le cadre de la transformation).

17voto

Chris Pitman Points 5981

Ce que vous avez besoin est une "boucle de message" qui prend la tâche suivante dans la file d'attente et l'exécute. En outre, chaque tâche doit être codées de sorte qu'il remplit autant de travail que possible sans blocage, puis place en file d'attente des tâches supplémentaires pour ramasser une tâche qui a besoin de temps plus tard. Il n'y a rien de magique à ce sujet: ne jamais utiliser un appel de blocage et de ne jamais spawn des threads supplémentaires.

Par exemple, lors du traitement d'une demande HTTP GET, le serveur peut lire que la quantité de données est actuellement disponible sur le socket. Si ce n'est pas assez de données pour répondre à la demande, puis de mise en file d'une nouvelle tâche à lire à partir de la prise de nouveau dans l'avenir. Dans le cas d'un FileStream, vous souhaitez définir le ReadTimeout sur l'instance à une faible valeur, et être prêt à lire de moins en moins d'octets que l'intégralité du fichier.

C# 5 en fait, fait ce modèle beaucoup plus triviales. Beaucoup de gens pensent que la async fonctionnalité implique le multithreading, mais qui n'est pas le cas. À l'aide de async, vous pouvez obtenir la tâche de la file d'attente je l'ai mentionné plus tôt, sans jamais explicility gestion.

10voto

Raynos Points 82706

Oui, il est appelé Manos de mono

Sérieusement, toute l'idée derrière manos est un seul thread asynchrone event driven web server.

Hautes performances et évolutif. Calquée sur tornadoweb, la technologie qui alimente ami nourrir, Manos est capable de milliers de connexions simultanées, idéal pour les applications qui créent des connexions persistantes avec le serveur.

Le projet semble être peu d'entretien et ne serait probablement pas prêt pour la production, mais il fait une bonne étude de cas comme une démonstration que c'est possible.

7voto

mythz Points 54874

Voici un excellent article de la série, expliquant ce qu'IO Ports de fin sont et comment ils peuvent être accessibles via C# (c'est à dire que vous devez PInvoke en API Win32 appels de la Kernel32.dll).

Remarque: Le libuv la croix-plate-forme IO cadre derrière node.js utilise IOCP sur Windows et libev sur les systèmes d'exploitation unix.

http://www.theukwebdesigncompany.com/articles/iocp-thread-pooling.php

2voto

martyonair Points 422

Je me demande personne n'a mentionné le kayak , il est basiquement la réponse de C # à Pythons tordu , JavaScripts Node.js ou Rubys eventmachine

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X