2008-08-28 27 views
26

Estoy escribiendo una aplicación que necesitará hacer uso de Timer s, pero potencialmente muchos de ellos. ¿Qué tan escalable es la clase System.Threading.Timer? La documentación simplemente dice que es "liviano", pero no explica más. ¿Se atrapan estos temporizadores en un solo hilo (o en un subproceso de subprocesamiento muy pequeño) que procesa todas las devoluciones de llamada en nombre de Timer, o cada Timer tiene su propio subproceso?¿Qué tan escalable es System.Threading.Timer?

Supongo que otra forma de reformular la pregunta es: ¿Cómo se implementa System.Threading.Timer?

Respuesta

29

lo digo en respuesta a una gran cantidad de preguntas: no olvide que el código fuente (administrado) para el marco está disponible. Puede utilizar esta herramienta para conseguirlo todo: http://www.codeplex.com/NetMassDownloader

Por desgracia, en este caso específico, una gran cantidad de la aplicación es en código nativo, por lo que no se llega a mirarlo ...

Ellos Sin embargo, definitivamente use hilos de grupo en lugar de un hilo por temporizador.

La forma estándar de implementar una gran colección de temporizadores (que es cómo lo hace internamente el kernel, y sospecho que es indirectamente cómo termina su gran colección de temporizadores) es mantener la lista ordenada por tiempo -hasta- vencimiento - por lo que el sistema solo tiene que preocuparse por comprobar el próximo temporizador que expirará, no toda la lista.

Aproximadamente, esto da O (log n) para iniciar un temporizador y O (1) para procesar temporizadores en ejecución.

Edit: Acabo de buscar en el libro de Jeff Richter. Él dice (de Threading.Timer) que usa una única secuencia para todos los objetos del temporizador, este subproceso sabe cuándo debe vencerse el siguiente temporizador (es decir, como el anterior) y llama a ThreadPool.QueueUserWorkItem para las devoluciones de llamada según corresponda. Esto tiene el efecto de que si no finaliza el mantenimiento de una devolución de llamada en un temporizador antes de que venza la siguiente, su devolución de llamada volverá a ingresar en otra secuencia de grupo. Así que, en resumen, dudo que veas un gran problema con tener muchos temporizadores, pero podrías sufrir agotamiento de la agrupación de subprocesos si gran cantidad de ellos se activan con el mismo temporizador y/o sus devoluciones se ejecutan lentamente.

+0

Una cola de prioridad probablemente sería más eficiente que una lista ordenada a menos que todos los temporizadores se agreguen al por mayor al principio, luego se clasifiquen y no se agreguen más después. – RAL

+0

Claro - 'una lista ordenada por tiempo hasta la expiración' podría ser ciertamente una especie de cola de prioridad - No quise dar a entender 'una lista en la que se ejecutó una operación de clasificación' –

+1

Acabo de pasar algo de tiempo sobre el código en el sscli. Tenga en cuenta que .NET ThreadPool ha cambiado enormemente desde que se lanzó Rotor, por lo que es muy posible que el System.Threading.Timer también haya cambiado enormemente. De hecho, los temporizadores se rompieron en .NET 1.1 y solo se corrigieron para ser seguros y de excepción en .NET 2.0 De todos modos, en Rotor los temporizadores se mantienen en una lista enlazada y disparados por un hilo de disparo del temporizador dedicado. Hay un hilo de activación de temporizador para todo el tiempo de ejecución (incluso en varios dominios de aplicación). –

7

Creo que es posible que desee replantear su diseño (es decir, si tiene control sobre el diseño). Si está utilizando tantos temporizadores que esto es realmente una preocupación para usted, claramente existe cierto potencial de consolidación allí.

Aquí es un buen artículo de MSDN Magazine de hace unos años que compara las tres clases de temporizador disponibles, y da una idea de sus implementaciones:

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

0

^^ as DannySmurf dice: Consolidate them. Crea un servicio de temporizador y pregúntale por los temporizadores. Solo necesitará mantener 1 temporizador activo (para la próxima llamada vencida) y un historial de todas las solicitudes de temporizador y recalcular esto en AddTimer()/RemoveTimer().

5

Consolidelas. Cree un servicio de temporizador y pregúntele por los temporizadores. Sólo se necesita para mantener 1 temporizador activo (para la próxima debido llamada) ...

Para que esto sea una mejora con respecto sólo la creación de una gran cantidad de objetos Threading.Timer, hay que asumir que ISN No es exactamente lo que Threading.Timer ya está haciendo internamente.Me interesaría saber cómo llegó a esa conclusión (no he desmontado los elementos nativos del marco, por lo que podría estar en lo cierto).