Pregunta

Actualmente, tengo una gran cantidad de cálculos de C# (llamadas a métodos) que residen en una cola que se ejecutará de forma secuencial.Cada cálculo utilizará algún servicio de alta latencia (red, disco...).

Iba a usar rutinas Mono para permitir que el siguiente cálculo en la cola de cálculo continuara mientras un cálculo anterior espera que regrese el servicio de alta latencia.Sin embargo, prefiero no depender de las rutinas Mono.

¿Existe algún patrón de diseño que se pueda implementar en C# puro y que me permita procesar cálculos adicionales mientras espero que regresen los servicios de alta latencia?

Gracias

Actualizar:

Necesito ejecutar una gran cantidad (>10000) de tareas, y cada tarea utilizará algún servicio de alta latencia.En Windows, no puedes crear tantos hilos.

Actualizar:

Básicamente, necesito un patrón de diseño que emule las ventajas (como se muestra a continuación) de los tasklets en Stackless Python (http://www.stackless.com/)

  1. Gran número de tareas
  2. Si una tarea bloquea la siguiente tarea en la cola se ejecuta
  3. Sin desperdicio de ciclo de CPU
  4. Cambio mínimo entre tareas
¿Fue útil?

Solución

Se puede simular microrrosca cooperativa utilizando IEnumerable. Por desgracia, esto no funcionará con el bloqueo de las API, por lo que necesita para encontrar API que puede sondear, o que tienen devoluciones de llamada que se pueden utilizar para la señalización.

Considere un método

IEnumerable Thread ()
{
    //do some stuff
    Foo ();

    //co-operatively yield
    yield null;

    //do some more stuff
    Bar ();

    //sleep 2 seconds
    yield new TimeSpan (2000);
}

El compilador de C # se desenvuelva esto en una máquina de estado -. Pero el aspecto es el de una MicroThread cooperativa

El patrón es bastante sencillo. Se implementa un "planificador" que mantiene una lista de todos los IEnumerators activos. Ya que los ciclos a través de la lista, "corre" cada uno usando MoveNext (). Si el valor de MoveNext es falsa, el mensaje ha terminado, y el programador lo elimina de la lista. Si es cierto, entonces el programador de acceso a la propiedad actual para determinar el estado actual de la rosca. Si se trata de un intervalo de tiempo, el hilo desea dormir, y el programador lo movió a alguna cola que se puede lavar de nuevo en la lista principal cuando los lapsos de sueño han terminado.

Puede utilizar otros objetos de retorno para implementar otros mecanismos de señalización. Por ejemplo, definir algún tipo de WaitHandle. Si el hilo produce uno de estos, se puede mover a una cola de espera hasta que se señaliza el mango. O bien, podría apoyar WaitAll al ceder una gran variedad de mangos de espera. Incluso se puede poner en práctica las prioridades.

Hice una aplicación simple de este planificador en aproximadamente 150LOC pero no he regresado a los blogs el código todavía. Fue por nuestra envoltura PhyreSharp PhyreEngine (que no será pública), en el que parece que funciona bastante bien para controlar un par de cientos de personajes en una de nuestras demostraciones. Hemos tomado prestado el concepto del motor Unity3D - que tienen algunos documentos en línea que lo explican desde el punto de vista del usuario

.

Otros consejos

Me gustaría recomendar el uso de la de subprocesos para ejecutar múltiples tareas de la cola a la vez en lotes manejables utilizando una lista de tareas activas que se alimenta de la cola de tareas.

En este escenario su subproceso de trabajo principal que inicialmente pop n tareas de la cola en la lista de tareas activas a ser enviado a la agrupación de hebras (lo más probable usando QueueUserWorkItem ), donde N representa una cantidad manejable que no se sobrecargue el grupo de subprocesos, pantano su aplicación hacia abajo con hilo de programación y de sincronización de los costos, o chupan la memoria disponible debido a la o sobrecarga de e combinado / memoria de cada tarea.

Siempre que una finalización señales de tareas para el subproceso de trabajo, puede eliminarlo de la lista de tareas activas y añadir el siguiente de la cola de tareas a ejecutar.

Esto le permitirá tener un conjunto de rodadura de n tareas de la cola. Puede manipular N afectar las características de rendimiento y encontrar lo mejor de sus circunstancias particulares.

Puesto que usted está en última instancia, un cuello de botella por operaciones de hardware (disco I / O y la red de E / S, CPU) Me imagino más pequeño es mejor. Dos tareas de grupo de subprocesos de trabajo en el disco E / S más probable es que no se ejecutará más rápido que uno.

También se puede aplicar flexibilidad en el tamaño y el contenido de la lista de tareas activas, limitándola a un número determinado de concreto tipo de tarea. Por ejemplo, si se está ejecutando en una máquina con 4 núcleos, puede encontrarse con que la configuración de más alto rendimiento es de cuatro tareas vinculadas a la CPU se ejecutan simultáneamente, junto con una tarea sobre el disco y una tarea a la red.

Si ya tiene una tarea clasificada como una tarea IO disco, puede optar por esperar hasta que se haya completado antes de añadir otra tarea IO de disco, y usted puede elegir para programar una tarea de CPU de ruedas o con destino a la red, en el ínterin .

Espero que esto tiene sentido!

PS:? ¿Tiene algún dependencias del orden de tareas

Se debería salir el concurrencia y coordinación en tiempo de ejecución . Una de sus muestras describe exactamente lo que estamos hablando: se llama a los servicios de latencia largo, y el CCR permite de manera eficiente alguna otra tarea a ejecutar mientras espera. Puede manejar gran número de tareas, ya que no es necesario que generará un subproceso para cada uno, a pesar de que utilizará todos sus núcleos, si se solicita a.

No es esto un uso convencional de procesamiento multi-hilo?

Tener un vistazo a los patrones como Reactor aquí

La escritura que utilice asíncrono IO podría ser suficiente.

Esto puede conducir a nasy, difícil de depurar el código y sin estructura fuerte en el diseño.

Usted debe echar un vistazo a esto:

http://www.replicator.org/node/80

Esto debería hacer exactamente lo que quiere. Es un truco, sin embargo.

Algunos más información sobre el patrón "reactiva" (como han mencionado en otra crítica) con respecto a una implementación en .NET; también conocido como "LINQ a Eventos"

http://themechanicalbride.blogspot.com /2009/07/introducing-rx-linq-to-events.html

-Oisin

De hecho, si se utiliza un hilo para una tarea, perderá el juego. Piense por qué Node.js puede soportar gran número de conexiones. El uso de un escaso número de hilo con async IO !!! Asíncrono y esperar funciones pueden ayudar en esto.

foreach (var task in tasks)
{
    await SendAsync(task.value);
    ReadAsync(); 
}

SendAsync () y ReadAsync () son falsos funciones para ASYNC llamada IO.

Tarea paralelismo es también un buen escoger. Pero no estoy seguro de cuál es más rápido. Puede probar ambos en su caso.

Sí, por supuesto que puede. Sólo se necesita para construir un mecanismo despachador que le devuelva la llamada en un lambda que usted proporciona y entra en una cola. Todo el código que escribo en la unidad utiliza este enfoque y nunca lo uso corrutinas. Envuelvo métodos que utilizan co-rutinas tales como enseres WWW para simplemente deshacerse de él. En teoría, corrutinas puede ser más rápido porque hay menos sobrecarga. Prácticamente introducen nueva sintaxis de un lenguaje para hacer una tarea bastante trivial y, además, no se puede seguir la traza de la pila correctamente en un error en una co-rutina, porque todo lo que ves es -> Siguiente. Tendrá que luego implementar la capacidad de ejecutar las tareas en la cola en otro hilo. Sin embargo, hay funciones paralelas en la última .NET y que estaría escribiendo esencialmente una funcionalidad similar. No sería muchas líneas de código en realidad.

Si alguien está interesado me envía el código, no lo tiene en mí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top