You are on page 1of 3

¿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


El modo “óptimo” de trabajo no es ni hacer algo cada vez que se reciban datos por un
un Timer ni un Thread. Puede ser determinado puerto. En este caso, interesaría
cualquier otra “cosa” con la condición crear un Thread que escuche en ese puerto (y que
de que no bloquee el método OnStart. 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.

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.