Est-il possible dans .NET, en utilisant C #, d’obtenir un modèle asynchrone basé sur les événements sans multithreading?

Je suis émerveillé par la conception architecturale de Node.js et je me demandais si C # était capable d’une telle conception:

Asynchrone, boucle événementielle / événementielle, E / S non bloquantes sans multithreading.

    Je pense que toutes les opérations BeginXyz qui implémentent le modèle de programmation asynchrone standard exécutent le rappel sur un thread de pool de threads, ce qui rend l’application automatiquement multithread.

    Toutefois, vous pouvez obtenir un modèle de programmation asynchrone mono-thread en synchronisant toutes les opérations via le seul thread d’interface graphique géré pour les applications Windows à l’aide de Control.Invoke ou plus généralement, SynchronizationContext .

    Chaque appel à BeginXyz devra être réécrit comme suit:

     // Start asynchronous operation here (1) var originalContext = SynchronizationContext.Current; obj.BeginFoo(ar => // Switch to the original thread originalContext.Post(ignored => { var res = obj.EndFoo(); // Continue here (2) })); 

    Le code marqué (2) continuera de s’exécuter sur le même thread que celui de (1), vous utiliserez donc le thread de pool de threads uniquement pour renvoyer la publication sur le thread (unique) d’origine.

    En passant, ceci est plus directement pris en charge par les stream de travail asynchrones en F # et peut être utilisé pour un style assez élégant de programmation par interface graphique, comme décrit ici . Je ne connais pas node.js , mais je suppose que vous pouvez également être surpris par les workflows asynchrones F #, car ils sont vraiment intéressants pour le style de programmation asynchrone / basé sur les événements / … 🙂

    Je travaille sur une telle chose dans .NET en tant que projet animalier. Je l’appelle ALE (Another Looping Event) … parce que la bière.

    C’est extrêmement alpha en ce moment, mais c’est tout à la maison parce que, comme vous, je voulais savoir si cela pouvait être fait.

    • C’est une architecture de boucle d’événement.
    • Il exploite les appels d’E / S asynchrones non bloquants de .NET
    • Il utilise des appels de style callback pour aider les développeurs à écrire du code un peu plus lisible.
    • Implémentation de sockets web async
    • Implémentation de serveur http async
    • Implémentation client Async SQL

    Contrairement à certains des autres essais de ce type, il n’est pas simplement un serveur Web doté d’une boucle d’événement. Vous pouvez écrire n’importe quelle application basée sur la boucle d’événement.


    Exemple

    Le code suivant va:

    • Démarrer la boucle d’événement
    • Démarrer un serveur Web sur le port 1337
    • Démarrer un serveur de socket Web sur le port 1338
    • Ensuite, lisez un fichier et “quelque chose” avec:
     EventLoop.Start(() => { //create a web server Server.Create((req, res) => { res.Write("

    Hello World

    "); }).Listen("http://*:1337"); //start a web socket server Net.CreateServer((socket) => { socket.Receive((text) => { socket.Send("Echo: " + text); }); }).Listen("127.0.0.1", 1338, "http://origin.com"); //Read a file File.ReadAllText(@"C:\Foo.txt", (text) => { DoSomething(text); }); });

    Donc, je suppose que ma réponse est “Oui”, cela peut être fait en C # … ou presque n’importe quelle langue d’ailleurs. Le vrai truc est de pouvoir tirer parti des E / S natives non bloquantes.

    Plus d’informations sur le projet seront affichées ici .

    Bien sûr, il suffit d’une boucle d’événement. Quelque chose comme:

     class EventLoop { List MyThingsToDo { get; set; } public void WillYouDo(Action thing) { this.MyThingsToDo.Add(thing); } public void Start(Action yourThing) { while (true) { Do(yourThing); foreach (var myThing in this.MyThingsToDo) { Do(myThing); } this.MyThingsToDo.Clear(); } } void Do(Action thing) { thing(); } } class Program { static readonly EventLoop e = new EventLoop(); static void Main() { e.Start(DoSomething); } static int i = 0; static void DoSomething() { Console.WriteLine("Doing something..."); e.WillYouDo(() => { results += (i++).ToSsortingng(); }); Console.WriteLine(results); } static ssortingng results = "!"; } 

    Bientôt, vous voudrez vous débarrasser de DoSomething et exiger que tout travail soit enregistré auprès de MyThingsToDo . Ensuite, vous voudrez passer une enum ou quelque chose à chaque ThingToDo qui lui dit pourquoi il fait quelque chose. À ce stade, vous réaliserez que vous avez une pompe à message .

    BTW, je dirais que node.js passe sous silence le fait qu’il tourne sur un système d’exploitation et une application multithread. Sans cela, chaque appel au réseau ou au disque se bloquerait.

    Reactive Extensions for .NET (Rx) est conçu pour la programmation asynchrone et parallèle. Il vous permet de programmer de manière réactive ou interactive, sans blocage. Vous utilisez les opérateurs de requête LINQ et de nouveaux opérateurs pour les interfaces IObservable / IObserver, qui font partie de Rx. Rx fournit le dual mathématique de IEnumerable / IEnumerator, sous la forme de IObservable / IObserver, ce qui signifie que vous pouvez utiliser tous les opérateurs de requête standard LINQ, de manière déclarative, par opposition à utiliser directement les API multithreading.

    Je ne sais pas si c’est ce que vous cherchez. .NET peut faire des callbacks asynchrones sans multi threading explicite.

    Votre exemple de node.js n’est pas vraiment applicable car le serveur sur lequel il s’exécute exécute tous les multithreads nécessaires. Si les événements sont exécutés en fonction du même signal d’horloge externe, ils ne sont pas asynchrones. Vous pouvez contourner ce problème en exécutant une autre application, ce qui créerait deux processus sur le système.

    Il est impossible de faire en sorte qu’une même application s’exécutant en tant que processus unique sur un seul système exécute des événements asynchrones sans avoir un autre thread.

    Pour plus de détails, voir cette question Asynchrone vs Multithreading .

    Vous pouvez utiliser le répartiteur WPF, même si vous ne faites pas d’interface utilisateur. Référencez l’assembly WindowsBase . Ensuite, dans votre code de démarrage, exécutez:

     Dispatcher.CurrentDispatcher.BeginInvoke(new Action(Initialize)); Dispatcher.Run(); 

    A partir de Initialize et ailleurs dans votre code, utilisez Dispatcher.CurrentDispatcher.BeginInvoke pour planifier l’exécution des méthodes asynchrones suivantes.

    J’ai développé un serveur basé sur HttpListener et une boucle d’événement, prenant en charge MVC, WebApi et le routage. Pour ce que j’ai vu, les performances sont bien meilleures que la norme IIS + MVC, pour le MVCMusicStore, je suis passé de 100 requêtes par seconde et 100% de processeurs à 350 avec 30% de processeurs. Si quelqu’un essaie, je me bats pour avoir des retours!

    PS toute suggestion ou correction est la bienvenue!

    • Documentation
    • Exemple de port MvcMusicStore sur Node.Cs
    • Forfaits sur Nuget

    Je crois que c’est possible, voici un exemple open-source écrit en VB.NET et C #:

    https://github.com/perrybutler/dotnetsockets/

    Il utilise un modèle asynchrone basé sur des événements (EAP) , un modèle IAsyncResult et un pool de threads ( IOCP ). Il sérialisera / marshalera les messages (les messages peuvent être n’importe quel object natif tel qu’une instance de classe) en paquets binarys, transférera les paquets sur TCP, puis désérialisera / annulera la déstructuration des paquets du destinataire afin que votre object natif puisse fonctionner avec . Cette partie est un peu comme Protobuf ou RPC.

    Il a été développé à l’origine comme un “netcode” pour les jeux multijoueurs en temps réel, mais il peut servir à de nombreuses fins. Malheureusement, je n’ai jamais eu le temps de l’utiliser. Peut-être que quelqu’un d’autre le fera.

    Le code source a beaucoup de commentaires donc il devrait être facile à suivre. Prendre plaisir!

    EDIT: Après des tests de résistance et quelques optimisations, il a été en mesure d’accepter 16 357 clients avant d’atteindre les limites du système d’exploitation. Voici les résultats:

     Simulating 1000 client connections over 16 iterations... 1) 485.0278 ms 2) 452.0259 ms 3) 495.0283 ms 4) 476.0272 ms 5) 472.027 ms 6) 477.0273 ms 7) 522.0299 ms 8) 516.0295 ms 9) 457.0261 ms 10) 506.029 ms 11) 474.0271 ms 12) 496.0283 ms 13) 545.0312 ms 14) 516.0295 ms 15) 517.0296 ms 16) 540.0309 ms All iterations complete. Total duration: 7949.4547 ms 

    Désormais, tous les clients s’exécutent sur localhost et envoient un petit message au serveur juste après la connexion. Lors d’un test ultérieur sur un système différent, le serveur atteignait un maximum de 64 000 connexions client (nombre de ports atteint!) À environ 2 000 par seconde, consommant 238 Mo de RAM.

    Voici mon exemple d’EAP à un seul thread. Il existe plusieurs implémentations d’EAP dans différents types d’application: de l’application console simple à l’application asp.net, au serveur TCP, etc. Ils sont tous construits sur le minuscule framework appelé SingleSand qui est théoriquement connectable à n’importe quel type d’application .net.

    Contrairement aux réponses précédentes, mon implémentation agit comme un médiateur entre les technologies existantes (asp.net, tcp sockets, RabbitMQ) d’un côté et les tâches de la boucle d’événements de l’autre côté. L’objective n’est donc pas de créer une application pure à un seul thread, mais d’intégrer la boucle d’événement aux technologies d’application existantes. Je suis tout à fait d’accord avec le post précédent que .net ne supporte pas les applications à un seul thread.