2010-05-16 19 views
13

Estoy sorprendido por el diseño arquitectónico de Node.js y se preguntaba si C# es capaz de este tipo de diseño:¿Es posible en .NET, usando C#, lograr un patrón asíncrono basado en eventos sin multihilo?

asincrónica, basada en eventos de bucle/evento, sin bloqueo I/O sin multithreading.

+0

¿'BeginRead' (http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx) logra eso? – Gabe

+1

Todas las E/S de red en Windows podrían ser asíncronas, basadas en eventos y sin multithreading en los días de 16 bits porque la programación de Windows está basada en eventos y Win16 no era multiproceso. – Gabe

+0

@Gabe, vea mi comentario en contra de la respuesta de @ Jeremy. – Jeff

Respuesta

13

Creo que todas las operaciones BeginXyz que implementan el modelo de programación asincrónica estándar ejecutan la devolución de llamada en un subproceso de grupo de subprocesos, lo que hace que la aplicación se multiplique automáticamente.

Sin embargo, puede lograr el modelo de programación asíncrono de una sola rosca sincronizando todas las operaciones a través del subproceso único de la GUI que se mantiene para aplicaciones de Windows usando Control.Invoke o más en general, SynchronizationContext.

Cada llamada a BeginXyz tendría que ser reescrito a lo largo de estas líneas:

// 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) 
    })); 

El código marcado como (2) continuará ejecutándose en la misma rosca que el código en (1), por lo que utilice el hilo de grupo de subprocesos solo para reenviar la devolución de datos al subproceso original (único).

Como nota al margen, esto es más directamente compatible con flujos de trabajo asincrónicos en F # y se puede utilizar para el estilo bastante elegante de la programación GUI as described here. No sé node.js, pero supongo que también es posible que se sorprenderá por los flujos de trabajo de F # asíncronos ya que son realmente fresco para asíncrono/evento basado/... estilo de programación :-)

2

No estoy seguro de si this es lo que está buscando. .NET puede hacer devoluciones de llamada asincrónicas sin multi hilos explícitos.

+1

No estoy 100% seguro, pero creo que bajo el capó .NET creará un nuevo hilo para ejecutar eventos de forma asíncrona. Sin embargo, podría estar equivocado. – Jeff

2

su ejemplo de node.js no es realmente aplicable ya que el servidor en el que se está ejecutando está realizando todos los multiprocesos necesarios. Si los eventos se ejecutan basándose en la misma señal de reloj externo, entonces no son asincrónicos. Puede evitar esto ejecutando otra aplicación y eso crearía dos procesos en el sistema.

No hay forma de que pueda hacer que la misma aplicación ejecutándose como un solo proceso en un solo sistema realice eventos asincrónicos sin tener otro hilo.

Para obtener más información, consulte este Asynchronous vs Multithreading question.

+0

Parece que necesito ensuciarme las manos y comenzar a descifrar el subyacente de las bibliotecas como máquina de eventos, twisted y node.js. Me interesa realmente entender cómo funcionan. – Jeff

3

El Reactive Extensions for .NET (Rx) está diseñado para programación asincrónica y paralela. Le permite programar de forma interactiva versus interactiva, sin bloqueo. Utiliza los operadores de consulta LINQ y los nuevos para interfaces IObservable/IObserver, que son parte de Rx. Rx proporciona el doble matemático de IEnumerable/IEnumerator, en la forma de IObservable/IObserver, lo que significa que puede usar todos los operadores de consultas estándar de LINQ, de manera declarativa, en lugar de usar las API de subprocesos múltiples directamente.

+0

Sin embargo, Rx tiene múltiples subprocesos, aunque, en principio, también podría admitir un modelo de programación de subproceso único. Mi implementación de la misma idea fue de un solo subproceso (¡pero limitada de muchas maneras!) Http://tomasp.net/blog/reactive-iv-reactivegame.aspx. También Rx ahora está disponible para JavaScript, y ciertamente es de un solo hilo :-). –

+1

Dado que hay mucha confusión sobre asynch que requiere o implica un multihilo, permítame dejar en claro que asynch no implica o requiere multithreading, y tampoco lo hace Rx. Ver, por ejemplo, http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx para obtener información sobre cómo funciona esto. –

6

Claro, solo requiere un ciclo de eventos. Algo así como:

class EventLoop { 
    List<Action> 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++).ToString(); 
     }); 
     Console.WriteLine(results); 
    } 

    static string results = "!"; 
} 

Muy pronto, tendrá que deshacerse de DoSomething y requieren todo el trabajo que estar registrado en MyThingsToDo.Luego, querrá pasar un enum o algo así para cada ThingToDo que le explique por qué está haciendo algo. En ese punto, se dará cuenta de que tiene un message pump.

Por cierto, yo diría que node.js está pasando por alto el hecho de que se está ejecutando en un sistema operativo y la aplicación que es multiproceso. Sin eso, cada llamada a la red o disco se bloquearía.

+0

es su ejemplo no bloqueante y asincrónico? Todo lo que veo es un programa que itera a través de una lista de tareas. Y las tareas o en forma de cierres. – Jeff

+0

@Jeffrey - sí. Difiere la llamada de bloqueo delegándola en EventLoop para que se ejecute, lo que significa que puede ejecutar sus propias tareas antes de la llamada de bloqueo. Si miramos más de cerca a node.js, en realidad * aprovechan múltiples hilos ("Node le dice al sistema operativo (a través de epoll, kqueue,/dev/poll o select) que debe ser notificado cuando los 2 segundos son arriba "), simplemente no los están creando explícitamente. –

+1

Creo que estoy totalmente confundido ahora. – Jeff

1

Puede usar el despachador de WPF, incluso si no está haciendo la interfaz de usuario. Consulte el conjunto WindowsBase. Luego, en el código de inicio ejecutar:

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

De Initialize y otras partes de su código, utilice Dispatcher.CurrentDispatcher.BeginInvoke para programar la ejecución de los métodos asincrónicos posteriores.

7

Estoy trabajando en tal cosa en .NET como un proyecto de mascota. Lo llamo ALE (Another Looping Event) ... porque la cerveza.

Es extremadamente alpha en este momento, pero es todo homebrew porque, como tú, quería saber si se puede hacer.

  • Es una arquitectura de bucle de eventos.
  • Aprovecha las llamadas de E/S asincrónicas sin bloqueo de .NET
  • Utiliza llamadas de devolución de llamada para ayudar a los desarrolladores a escribir código asíncrono que es un poco más legible.
  • tomas asíncrono web implementación
  • servidor http asíncrono implemenation cliente aplicación
  • asíncrono sql

A diferencia de algunos de los otros intentos en este he visto que no es sólo un servidor web con un bucle de eventos. Podría escribir cualquier tipo de aplicación basada en bucle de eventos a partir de esto.


Ejemplo

El siguiente código:

  • iniciar el bucle de eventos
  • iniciar un servidor web en el puerto 1337
  • Iniciar un servidor de socket web en el puerto 1338
  • Luego lea un archivo y "Haga algo" con que:
EventLoop.Start(() => { 

    //create a web server 
    Server.Create((req, res) => { 
     res.Write("<h1>Hello World</h1>"); 
    }).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); 
    }); 
}); 

Así que supongo que mi respuesta es "Sí", se puede hacer en C# ... o casi cualquier lenguaje para el caso. El verdadero truco es poder aprovechar las E/S nativas sin bloqueo.

Se publicará más información sobre el proyecto en here.

0

desarrollé un servidor basado en HttpListener y un bucle de eventos, soportando MVC, WebApi y enrutamiento.Por lo que he visto, los rendimientos son mucho mejores que el estándar IIS + MVC, para MVCMusicStore pasé de 100 solicitudes por segundo y 100% de CPU a 350 con 30% de CPU. ¡Si alguien lo intenta, estoy luchando por obtener retroalimentación!

PS ¡cualquier sugerencia o corrección es bienvenida!

0

creo que es posible, aquí es un ejemplo de código abierto escrito en VB.NET y C#:

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

Usa Event-based Asynchronous Pattern (EAP), IAsyncResult Pattern y grupo de subprocesos (IOCP). Enviará/clasificará los mensajes (los mensajes pueden ser cualquier objeto nativo como una instancia de clase) en paquetes binarios, transferirá los paquetes a través de TCP y luego deserializará/desempaquetará los paquetes en el extremo receptor para que pueda trabajar con su objeto nativo . Esta parte es algo así como Protobuf o RPC.

Originalmente se desarrolló como un "código de red" para juegos multijugador en tiempo real, pero puede servir para muchos propósitos. Lamentablemente, nunca llegué a usarlo. Tal vez alguien más lo hará.

El código fuente tiene muchos comentarios, por lo que debería ser fácil de seguir. ¡Disfrutar!

EDITAR: Después de las pruebas de estrés y algunas optimizaciones, pudo aceptar 16,357 clientes antes de alcanzar los límites del OS. Aquí están los resultados:

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 

ahora que es con todos los clientes que se ejecutan en la máquina local y enviar un pequeño mensaje al servidor justo después de la conexión. En una prueba posterior en un sistema diferente, el servidor se estaba agotando en un poco más de 64,000 conexiones de cliente (¡límite de puerto alcanzado!) A aproximadamente 2000 por segundo, consumiendo 238 MB de RAM.

+0

Técnicamente hablando, esto sigue siendo multithreading, solo que las bibliotecas son haciéndolo por ti. –

+0

Gracias por aclarar eso, usted es técnicamente correcto. Sin embargo, es el mismo método que utiliza Node.js para manejar las terminaciones (grupo de hilos/IOCP) en Windows, al menos eso creo, lo que significa que Node.js tampoco es realmente un único subproceso. Por lo tanto, creo que C# es capaz de un diseño similar a Node.js, en un intento por responder la pregunta original. Si bien el grupo de subprocesos/IOCP no implica el uso de más de un único subproceso, todavía no se considera un "servidor de subprocesos". ¿Qué piensas? – perry

+0

Tiene razón, bajo el nodo Node [utiliza un grupo de subprocesos basado en C++ para tareas de larga ejecución] (http: // rickgaribay.net/archive/2012/01/28/node-is-not-single-threaded.aspx). No era consciente. (De repente, parece mucho menos interesante desde el punto de vista arquitectónico ...) –

0

Aquí está my example de EAP de un solo hilo. Hay varias implementaciones de EAP en diferentes tipos de aplicaciones: desde la aplicación de consola simple hasta la aplicación asp.net, el servidor tcp y más. Todos están basados ​​en el pequeño marco llamado SingleSand que teóricamente es conectable a cualquier tipo de aplicación .net.

En contraste con las respuestas anteriores, mi implementación actúa como mediador entre las tecnologías existentes (asp.net, tcp sockets, RabbitMQ) de un lado y las tareas dentro del ciclo de eventos del otro lado. Por lo tanto, el objetivo no es crear una aplicación pura de un único subproceso, sino integrar el ciclo de eventos a las tecnologías de aplicaciones existentes. Estoy completamente de acuerdo con la publicación anterior de que .net no es compatible con aplicaciones de subproceso único puro.

Cuestiones relacionadas