c++ multithreading scheduled-tasks threadpool

c++ - Gestor de colas de trabajos multiproceso



multithreading scheduled-tasks (10)

¿Algo como threadpool te sería útil? Se basa en boost :: thread y básicamente implementa una cola de tareas de hilo simple que transfiere las funciones de los trabajadores a los hilos agrupados.

Necesito gestionar trabajos multitarea pesados ​​en CPU en una aplicación interactiva. Solo como fondo, mi aplicación específica es una interfaz de diseño de ingeniería. A medida que el usuario ajusta diferentes parámetros y opciones a un modelo, se ejecutan varias simulaciones en segundo plano y los resultados se muestran a medida que se completan, probablemente incluso cuando el usuario todavía está editando valores. Dado que las simulaciones múltiples tardan un tiempo variable (algunas son milisegundos, algunas tardan 5 segundos, otras tardan 10 minutos), básicamente se trata de obtener la retroalimentación lo más rápido posible, pero a menudo cancela trabajos que comenzaron anteriormente pero ya no son necesarios porque de los cambios del usuario ya los han invalidado. Diferentes cambios de usuario pueden invalidar diferentes cálculos, por lo que en cualquier momento puedo tener 10 simulaciones diferentes en ejecución. Las simulaciones tienen varias partes que tienen dependencias (las simulaciones A y B se pueden calcular por separado, pero necesito sus resultados para simular la semilla C, así que tengo que esperar a que A y B terminen primero antes de iniciar C.)

Me siento bastante seguro de que el método de nivel de código para manejar este tipo de aplicación es algún tipo de cola de trabajos multiproceso. Esto incluiría características de enviar trabajos para su ejecución, establecer prioridades de tareas, esperar a que terminen los trabajos, especificar dependencias (hacer este trabajo, pero solo después de que hayan terminado el trabajo X y el trabajo Y), cancelar subconjuntos de trabajos que cumplan algunos criterios, consultar qué los trabajos permanecen, estableciendo los recuentos y prioridades de subprocesos de trabajador, y así sucesivamente. Y el soporte multiplataforma también es muy útil.

Estas no son ideas o deseos nuevos en el software, pero estoy en la fase de diseño inicial de mi aplicación, donde tengo que elegir qué biblioteca usar para administrar tales tareas. En el pasado, escribí mis propios gestores de hilos crudos (creo que es un rito de iniciación), pero quiero usar herramientas modernas en las que basar mi trabajo, no mis propios hacks previos.

La primera idea es ejecutar OpenMP pero no estoy seguro de que sea lo que quiero. OpenMP es ideal para paralelizar en un nivel fino, desenrollar bucles automáticamente y tal. Mientras multiplataforma, también invade tu código con #pragmas. Pero sobre todo no está diseñado para administrar grandes tareas ... especialmente cancelando trabajos pendientes o especificando dependencias. Posible, sí, pero no es elegante.

Observé que Google Chrome usa dicho gestor de trabajos incluso para las tareas más triviales. El objetivo del diseño parece ser mantener el hilo de interacción del usuario lo más ligero y ágil posible, por lo que todo lo que pueda generarse de forma asincrónica debería serlo. Al mirar la fuente de Chrome esto no parece ser una biblioteca genérica, pero sigue siendo interesante ver cómo el diseño usa lanzamientos asincrónicos para mantener la interacción rápida. Esto se está volviendo similar a lo que estoy haciendo.

Todavía hay otras opciones:

Surge.Act: una biblioteca similar a Boost para definir trabajos. Se basa en OpenMP, pero permite el encadenamiento de dependencias, lo que es bueno. No parece que tenga un gerente que pueda ser consultado, trabajos cancelados, etc. Es un proyecto obsoleto, por lo que es aterrador depender de él.

Job Queue está bastante cerca de lo que estoy pensando, pero es un artículo de 5 años, no una biblioteca compatible.

Boost.threads tiene una buena sincronización independiente de plataforma, pero no es un administrador de trabajo. POCO tiene diseños muy limpios para el lanzamiento de tareas, pero nuevamente no es un administrador completo para las tareas de encadenamiento. (Tal vez estoy subestimando a POCO).

Entonces, aunque hay opciones disponibles, no estoy satisfecho y siento la necesidad de rodar mi propia biblioteca de nuevo. Pero prefiero usar algo que ya existe. Incluso después de buscar (aquí en SO y en la red) no he encontrado nada que se sienta bien, aunque me imagino que debe ser un tipo de herramienta que a menudo se necesita, así que seguramente hay alguna biblioteca comunitaria o al menos diseño común. En SO ha habido algunas publicaciones sobre colas de trabajos , pero nada parece encajar.

Mi publicación aquí es para preguntarte qué herramientas existentes he omitido y / o cómo has rodado tu propia cola de trabajos multiproceso.


Eche un vistazo a boost :: future (pero vea también esta discusión y propuesta ) que parece una buena base para el paralelismo (en particular, parece ofrecer un excelente soporte para situaciones de tipo C-depende-de-A-y-B) .

Miré OpenMP un poco, pero (como usted) no estaba convencido de que funcionaría para nada más que el código numérico Fortran / C. Los bloques de construcción Threading de Intel me parecieron más interesantes.

Si se trata de eso, no es demasiado difícil implementar uno propio sobre boost :: thread. [Explicación: una granja de servidores de subprocesos (la mayoría de las personas lo llamarían un conjunto) extrae el trabajo de una cola segura de subprocesos de funtores (tareas o trabajos). Consulte las pruebas y el punto de referencia para ver ejemplos de uso. Tengo algunas complicaciones adicionales para (opcionalmente) apoyar tareas con prioridades, y el caso en que la ejecución de tareas puede generar más tareas en la cola de trabajo (esto hace que saber cuándo todo el trabajo se haya completado sea un poco más problemático; las referencias a "pendiente" son los que pueden manejar el caso). Podría darle algunas ideas de todos modos.]


Es posible que desee ver la Programación basada en flujo : está basada en fragmentos de datos que se transmiten entre componentes asíncronos. Hay versiones de Java y C # del controlador, más una cantidad de componentes precodificados. Es intrínsecamente multiproceso: de hecho, el único código de un único subproceso se encuentra dentro de los componentes, aunque puede agregar restricciones de tiempo a las reglas de programación estándar. Aunque puede estar en un nivel muy fino para lo que necesita, puede haber cosas aquí que pueda usar.


Rodé el mío, basado en Boost.threads. Me sorprendió bastante lo mucho que conseguí escribiendo tan poco código. Si no encuentras algo prefabricado, no tengas miedo de hacer tu propio. Entre Boost.threads y tu experiencia desde que escribiste la tuya, podría ser más fácil de lo que recuerdas.

Para las opciones prefabricadas, no olvide que Chromium tiene una licencia muy amigable, por lo que puede rodar su propia biblioteca genérica alrededor de su código.



Hay muchos administradores de recursos distribuidos por ahí. El software que cumple con casi todos sus requisitos es Sun Grid Engine . SGE se utiliza en algunas de las supercomputadoras más grandes del mundo y está en desarrollo activo.

También hay soluciones similares en Torque , Platform LSF y Condor .

Parece que querrá rodar el suyo, pero hay una gran cantidad de funcionalidad en todo lo anterior.


Microsoft está trabajando en un conjunto de tecnologías para la próxima Versión de Visual Studio 2010 llamada Concurrency Runtime, Parallel Pattern Library y Asynchronous Agents Library, que probablemente le ayuden. Concurrency Runtime ofrecerá programación basada en políticas, es decir, que le permite administrar y componer varias instancias del planificador (similar a grupos de subprocesos pero con afinización y equilibrio de carga entre instancias), Parallel Pattern Library ofrecerá programación basada en tareas y bucles paralelos con un STL como modelo de programación. La biblioteca Agentes ofrece un modelo de programación basado en el actor y tiene soporte para la construcción de conductos de flujo de datos concurrentes, es decir, la gestión de las dependencias descritas anteriormente. Lamentablemente, esto aún no se ha publicado, por lo que puede leerlo en el blog de nuestro equipo o ver algunos de los videos en channel9. También hay un CTP muy grande que también está disponible para su descarga.

Si está buscando una solución hoy, los bloques de construcción de subprocesos de Intel y la biblioteca de subprocesos de boost son buenas bibliotecas y están disponibles ahora. JustSoftwareSolutions ha lanzado una implementación de std :: thread que coincide con el borrador de C ++ 0x y, por supuesto, OpenMP está ampliamente disponible si se busca un paralelismo basado en bucles de grano fino.

El verdadero desafío al que otras personas han aludido es identificar y descomponer correctamente el trabajo en tareas adecuadas para la ejecución concurrente (es decir, sin estado compartido sin protección), comprender las dependencias entre ellos y minimizar la contención que puede ocurrir en cuellos de botella (si el cuello de botella protege estado compartido o asegurando que el ciclo de despacho de una cola de trabajo sea de baja contención o esté libre de bloqueos) ... y para hacer esto sin programar detalles de implementación que se filtren en el resto de su código.

-Almiar


No sé si estás buscando una biblioteca C ++ (que creo que eres), pero el framework Fork / Join de Doug Lea para Java 7 es bastante ingenioso, y hace exactamente lo que quieres. Probablemente pueda implementarlo en C ++ o encontrar una biblioteca pre implementada.

Más información aquí: http://artisans-serverintellect-com.si-eioswww6.com/default.asp?W1


Tuvimos que construir nuestro propio sistema de cola de trabajos para cumplir requisitos similares a los tuyos (el hilo de UI siempre debe responder dentro de 33ms, los trabajos pueden ejecutarse desde 15-15000ms), porque realmente no había nada por ahí que satisficiera nuestras necesidades, y mucho menos fuera el rendimiento .

Desafortunadamente, nuestro código es tan exclusivo como el de propiedad, pero puedo darle algunas de las características más destacadas:

  • Comenzamos un hilo por núcleo al principio del programa. Cada uno extrae el trabajo de una cola de trabajos global. Los trabajos consisten en un objeto de función y un conjunto de datos asociados (realmente una elaboración en un func_ptr y void *). El subproceso 0, el bucle de cliente rápido, no tiene permiso para trabajar en trabajos, pero el resto toma lo que puede.
  • La cola de trabajos en sí debe ser una estructura de datos sin bloqueos, como una lista individualmente vinculada y sin candados (Visual Studio viene con uno ). Evite usar un mutex; la contención para la cola es sorprendentemente alta, y agarrar mutexes es costoso.
  • Empaque todos los datos necesarios para el trabajo en el objeto de trabajo en sí: evite que el puntero del trabajo vuelva al montón principal, donde tendrá que lidiar con la contención entre trabajos y bloqueos y todas esas otras cosas lentas e irritantes. Por ejemplo, todos los parámetros de simulación deben ir al blob de datos locales del trabajo. La estructura de resultados, obviamente, necesita ser algo que sobreviva al trabajo: puede abordar esto ya sea a) aferrándose a los objetos del trabajo incluso después de que hayan terminado de ejecutarse (para que pueda usar sus contenidos del hilo principal), o b) asignando una estructura de resultados especialmente para cada trabajo y metiendo un puntero en el objeto de datos del trabajo. A pesar de que los resultados en sí mismos no vivirán en el trabajo, esto efectivamente le da al trabajo acceso exclusivo a su memoria de salida, por lo que no es necesario tener bloqueos.

  • En realidad, estoy simplificando un poco más arriba, ya que necesitamos coreografiar exactamente qué trabajos se ejecutan en qué núcleos, por lo que cada núcleo obtiene su propia cola de trabajos, pero eso probablemente sea innecesario para usted.