¿TIMERS O THREADS PARA EJECUCIÓN DE CÓDIGO EN SERVICIOS WINDOWS?

Nivel: Intermedio-Avanzado

por Alberto Población

Esta pregunta apareció hace un par de meses en uno de los foros de .Net:
“Estoy empezando un Windows Service, y veo que la mayoría de los ejemplos en el Framework 3.5 utilizan el objeto System.Timers.Timer. Este objeto tiene la capacidad de levantar un evento cuando pasa cierto periodo de tiempo desde que se activa, pero he trabajado con Windows Services en Frameworks anteriores al 3.5, por ejemplo 1.x y se utilizaban Threads para realizar esta tarea. ¿Cuál de estos dos métodos sería el ideal en el Framework 3.5?"

La respuesta no es inmediata, porque el usuario que formula la pregunta tiene una confusión considerable respecto a cómo funcionan los servicios, y los cambios producidos entre versiones del Framework. Respecto a la versión de Framework, lo primero que hay que señalar es que en este caso ésta resulta irrelevante. De cara al tema de la pregunta, no ha cambiado nada entre la versión 1.x y la 3.5. Los servicios siguen funcionando igual, y lo que era aplicable en la 1.x sigue siéndolo en la 3.5. Un requisito fundamental que tenemos que tener en cuenta al programar el servicio es que el método OnStart, que se ejecuta en el momento en que el servicio se pone en marcha, debe terminar rápidamente. Si tarda más de unos segundos, el Service Control Manager (SCM) mostrará un error indicando que nuestro servicio no consiguió arrancar. Normalmente escribimos el servicio con la Un requisito fundamental al construir intención de que permanezca activo, ejecutando un servicio Windows es que el método determinadas tareas. Por lo tanto, estas tareas no OnStart debe terminar rápidamente. deben de ejecutarse dentro del OnStart, ya que Por lo tanto el código del servicio en ese caso su ejecución no terminaría en pocos debe ejecutarse en un hilo distinto. segundos. Lo que hay que hacer en el OnStart es poner en marcha algún mecanismo que, cuando sea requerido por nuestro servicio, ejecute el trabajo correspondiente en un hilo distinto del que utilizó el SCM para ejecutar el OnStart. La forma de conseguir esta ejecución de código en un hilo separado dependerá del objetivo de nuestro Servicio, no de la versión del Framework que se utilizó para compilarlo. Si se necesita hacer algo con una periodicidad fija, entonces interesa utilizar Timer, pero no siempre es el caso. Por ejemplo, podríamos tener un servicio que haga "algo" cada vez que se copia un fichero a una determinada carpeta. En este caso, nos interesa emplear un FileSystemWatcher para

que dispare automáticamente un evento cada vez que se produzca esa copia de fichero. En ese evento meteríamos nuestro código, y el OnStart lo único que haría es inicializar los parámetros del FileSystemWatcher. También podría ser que el servicio tuviera que hacer algo cada vez que se reciban datos por un determinado puerto. En este caso, interesaría crear un Thread que escuche en ese puerto (y que la mayor parte del tiempo estará parado, esperando a que se realice la operación de entrada/salida). El OnStart símplemente inicializaría el Thread, y la rutina lanzada dentro de ese thread consistiría en un bucle infinito esperando a recibir datos y procesando los datos recibidos. El modo “óptimo” de trabajo no es ni un Timer ni un Thread. Puede ser cualquier otra “cosa” con la condición de que no bloquee el método OnStart. Como vemos, el modo “óptimo” de trabajo no es ni un Timer ni un Thread. Puede ser cualquier cosa, dependiendo de la finalidad de nuestro Servicio, y con la condición de que no bloquee el OnStart. Presentamos a continuación un ejemplo de servicio basado en un FileSystemWatcher, que como se ve no utiliza ni un Thread ni un Timer, y sin embargo vigila perfectamente los cambios en el sistema de archivos, guardando en un archivo de “log” una línea informativa cada vez que se borra un archivo:
public partial class Service1 : ServiceBase { private const string directorio = @"C:\Temp"; private FileSystemWatcher fsw; public Service1() { InitializeComponent(); if (!Directory.Exists(directorio)) Directory.CreateDirectory(directorio); fsw = new FileSystemWatcher(); fsw.Path = directorio; fsw.IncludeSubdirectories = true; fsw.Filter = "*.txt"; fsw.Deleted += new FileSystemEventHandler(fsw_Deleted); } protected override void OnStart(string[] args) { fsw.EnableRaisingEvents = true; } protected override void OnStop() { fsw.EnableRaisingEvents = false; }
Continúa...

void fsw_Deleted(object sender, FileSystemEventArgs e) { using (StreamWriter sw = new StreamWriter(directorio+"\\log.txt", true)) { sw.WriteLine(DateTime.Now.ToString()+": Se ha borrado el archivo " + e.FullPath); } } }

Acerca del autor
Alberto Población lleva 27 años desarrollando software. Ha sido reconocido por Microsoft como MVP (Most Valuable Professional) de C#. Cuenta, entre otras, con las certificaciones MCT, MCSE, MCDBA, MCITP, MCSD y MCPD en sus tres variantes (Desarrollador Web, Desarrollador Windows y Desarrollador de Aplicaciones Empresariales). En la actualidad se dedica principalmente a la formación, asesoramiento y desarrollo de aplicaciones. Es tutor de campusMVP.

Acerca de campusMVP
CampusMVP te ofrece la mejor formación en tecnología Microsoft a través de nuestros cursos online y nuestros libros especializados, impartidos y escritos por conocidos MVP de Microsoft. Visita nuestra página y prueba nuestros cursos y libros gratuitamente. www-campusmvp.com

Reconocimiento - NoComercial - CompartirIgual (by-nc-sa): No se permite un uso comercial de este documento ni de las posibles obras derivadas, la distribución de las cuales se debe hacer con una licencia igual a la que regula esta obra original. Se debe citar la fuente.

Sign up to vote on this title
UsefulNot useful