C# WinServiceBasic1: Crear un servicio básico en Windows

Los servicios (o demonio en Linux) son normalmente aplicaciones que corren de fondo (sin interfaz) y ofrecen como su nombre lo dice un servicio a otras aplicaciones, es muy común por ejemplo para implementar  protocolos de comunicaciones en red con otras máquinas mediante sockets TCP/UDP actuando como un servidor.  Otro uso muy común es como servidor de acceso a datos alojados en una máquina central.

Código del ejercicio en GitHub

Crear un servicio en VS 2012

En Visual Studio ahora te facilita crear aplicaciones de este tipo ya que ofrece un tipo de proyecto especifico “Windows Service”:

Visual Studio añade automáticamente una clase nueva Service1.cs heredada de System.ServiceProcess.ServiceBase.

Cambiamos el nombre de “Service1” por “MyNewService” buscando y reemplazando en todo el proyecto,

Añadir eventos de Windows

En la vista de diseño de Service1.cs arrastramos y añadimos un control “EventLog

VS le asigna un nombre eventLog1 a la nueva clase, podemos cambiar el nombre de la clase en las propiedades.

Ahora ya podemos añadir eventos para notificar de arranque / parada del servicio.

Instalar el servicio

Para crear un instalador usamos el botón derecho del ratón en la vista diseño Service1.cs sobre el fondo y el menú contextual seleccionamos “Add Installer”, revisamos las propiedades de “serviceInstaller1”, ServiceName debe estar definido como “MyNewService”, es importante elegir el tipo de arranque (lo dejamos en manual por el momento).

Para instalar el servicio hay que hacerlo mediante el interprete de comandos “Developer Command Prompt” incluido en VS.

Ahora vamos a la carpeta de salida donde está WinServiceBasic1.exe y ejecutamos:

installutil.exe WinServiceBasic1.exe

 

Anuncios

C# ThreadingBasics (II): Operaciones básicas con Threads

Código en GitHub

GetThreadState: Determinar el estado de un thread

El siguiente ejercicio nos permite obtener información valiosa sobre el estado de un Thread. Añadimos los siguientes métodos:t1

Y añadimos el siguiente código a la función principal Main:

t1

El Thread pasa por varios estados, podemos obtener el estado del hilo accediendo a la propiedad ThreadState que es un campo enumerado.

Inicialmente el estado del Thread t1 es Unstarted antes de ser arrancado. Cuando invocamos el método Start() inmediatamente pasa al estado Running. La llamada a Sleep provoca que pase al estado WaitSleepJoin. Finalmente la llamada Abort hace que pase a estado Aborted. El thread t2 finaliza con normalidad y su estado estado final es Stopped.

TheadPriority: Establecer prioridad en la ejecución de un thread


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Threading;

namespace TheadPriority
{
class ThreadSample
{
private bool _isStopped = false;

public void Stop()
{
this._isStopped = true;
}

public void CountNumbers()
{
long counter = 0;
while (!this._isStopped)
{
counter++;
}
Console.WriteLine("{0} with {1,11} priority " +
"has a count = {2,13}",
Thread.CurrentThread.Name,
Thread.CurrentThread.Priority,
counter.ToString("N0"));
}
}

class Program
{
static void RunThreads()
{
var sample = new ThreadSample();
var t1 = new Thread(sample.CountNumbers);
t1.Name = "t1";
var t2 = new Thread(sample.CountNumbers);
t2.Name = "t2";

t1.Priority = ThreadPriority.Highest;
t2.Priority = ThreadPriority.Lowest;

t1.Start();
t2.Start();

Thread.Sleep(TimeSpan.FromSeconds(10));
sample.Stop();
}

static void Main(string[] args)
{
Console.WriteLine("Main: current thread priority: {0}",
Thread.CurrentThread.Priority);
Console.WriteLine("Main: Calling RunThreads()");
RunThreads();
Console.WriteLine("Press any key to exit....");
Console.ReadLine();
}
}
}

C# ThreadingBasics: Operaciones básicas con threads

Creando un thread en C#

Vamos a crear una aplicación de consola. Añadimos el espacio de nombres using System.Threading;. Creamos una función static PrintNumbers (los hilos no retornan nada ni reciben parámetros) que escribe por consola del 1 al 10 usando un bucle for.

t1

Una instancia de un programa en ejecución se puede definir como un proceso. Un proceso consiste en uno o más threads. Esto significa que cuando ejecutamos un programa, siempre tenemos un thread principal que ejecuta el código del programa.

El método PrintNumbers lo invocaremos dos veces, desde el Main y como thread o hilo concurrente.

t2

Pausando un thread

En el siguiente ejemplo dentro de la función PrintNumbersWithDelay hacemos una llamada al método Thread.Sleep que  suspende la ejecución durante 2 segundos (usamos la estructura TimeSpan para con el método  FromSeconds para convertir de segundos en milisegundos, la magnitud reconocida por el método Sleep).

t1

Haciendo que un thread espere

En este caso vamos a hacer que la función principal Main detenga su ejecución  y espere a que el el thread acabe. Usamos el método Thread.Join()  que bloquea el subproceso de llamada hasta que finaliza el subproceso representado por esta instancia, si le pasásemos un valor al método esperaría el tiempo especificado a que el thread finalice.

t1Abortando la ejecución de un thread

Ahora el hilo principal Main espera 6 segundos y aborta la ejecución del thread con el método Abort() que lanza la excepción ThreadAbortException en el subproceso en el que se invoca.

t1 No es muy recomendable usar el método Abort ya que el resultado puede ser incierto.

 

C# GuiUpdateThread: Refrescar el contenido de la ventana desde un subproceso

En ocasiones cuando desarrollamos aplicaciones de ventanas es necesario desarrollar un proceso largo, si lo hacemos de la forma clásica puede suceder que la interfaz se bloquee, lo ideal en estos casos lo ideal es usar subprocesos.

Vamos a emplear una clase Thread para actualizar el contenido de un campo de edición en un formulario. El thread arranca cuando pulsamos un botón y activa un contador de 0 a 100 con un Sleep en cada ejecución del bucle, cada iteración refresca el contenido del campo de edición con el valor del contador.

t1

Controles:

  • btnStartThread: Botón con leyenda “&StartThread” y diseño plano, cuando hacemos clic sobre el desencadena el evento btnStartThread_Click que ejecuta el subproceso txtNumUpdateThread.
  • txtNum: Campo de edición deshabilitado (Enabled=False).

Cuando pulsamos el botón programamos el arranque del evento:

t2

Un hilo es una instancia de la clase Thread, al crear la instancia debemos pasar como parámetro al constructor la instancia de un delegado (ThreadStart), el delegado apunta al método que corre en segunda plano actualizando el contenido del campo de edición txtNum (txtNumUpdateThread).

Ahora creamos el método txtNumUpdateThread, este será el encargado de refrescar el contenido del campo de edición:

t3

Cuando tenemos actualizar un componente del interfaz gráfico desde un método en un hilo separado en vez de escribir simplemente:

txtNum.Text = x.ToString();

Debemos usar el método Invoke del control (la propiedad InvokeRequired del control contiene true si debemos usar un método de invocación).

“El acceso a los controles de Windows Forms no es intrínsecamente seguro para subprocesos. Si tiene dos o más subprocesos que manipulan el estado de un control, es posible que este acabe teniendo un estado incoherente”

Podemos comprobarlo añadiendo algo de código a la función anterior:

t4

 

GuiUpdateThread2: Segundo ejemplo


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Threading;

namespace GuiUpdateThread2
{
public partial class Form1 : Form
{
// delegado con la misma firma del método
public delegate void UpdateTextCallback(string msg);

public Form1()
{
InitializeComponent();
}

private void UpdateTxtMsg(string StrMsg)
{
TxtMsg.Text = StrMsg;
}

private void UpdateTxtThread()
{
string StrTxt = "Texto generado desde otro hilo.";
if (TxtMsg.InvokeRequired)
{
TxtMsg.Invoke(new UpdateTextCallback(this.UpdateTxtMsg),
new object[] { StrTxt });
}
else
{
TxtMsg.Text = StrTxt;
}
}

private void button1_Click(object sender, EventArgs e)
{
ThreadStart delegado = new ThreadStart(UpdateTxtThread);
Thread hilo = new Thread(delegado);
hilo.Start();
}
}
}

 

Posts en este blog:

Enlaces externos:

 

C# MsgPrinterThead: Subprocesamiento múltiple

Introducción

Poder realizar varias operaciones en paralelo de o de forma concurrente es esencial en cualquier lenguaje de programación cuando construimos aplicaciones avanzadas. La biblioteca de clases de .NET framework cuenta con primitivas para el subprocesamiento mútiple. El espacio de nombres (namespace) que debemos incluir es System.Threading.

Los subprocesos transitan por varios estados durante su ciclo de vida. Dos clases básicas para el subprocesamiento del namespace System.Threading son Thread y Monitor.

thread

Un objeto Thread inicialmente está inactivo (Unstarted) cuando el programa crea el objeto (un objeto es una instancia de una clase) y pasa un delegado ThreadStart al constructor del objeto.

thread1

El delegado ThreadStart permite especificar la acción que realizará el proceso durante su ciclo de vida debe declararse como un método que no retorne (void) y sin parámetros:

thread2

El subproceso se mantiene en estado Unstarted hasta que llamamos al método Start de la clase Thread que arranca el hilo multitarea (Running). thread3

En ese momento ejecuta el método especificado por su delegado ThreadStart. Podemos forzar la parada del subproceso en cualquier momento llamando al método Abort. El método Abort lanza una excepción ThreadAbortException en el subproceso.

Primer programa con tareas concurrentes

La aplicación crea 3 hilos que imprimen un mensaje por consola antes y después de pasar por un Sleep de un número de milisegundos aleatorio para cada método.

Creamos un nuevo proyecto de consola y le damos nombre “MsgPrinterThead” por ejemplo, inicialmente antes de comenzar:

 thread4

Lo primero es incluir el espacio de nombres System.Threading.

Clase clsMsgPrinterThead

Vamos a crear una nueva clase dentro del espacio de nombres y la llamaremos “clsMsgPrinterThead”, la clase contiene dos miembros privados para contener el número aleatorio que generemos en el constructor de la clase y que será el valor empleado para pausar la ejecución del método Print llamado como subproceso.

thread5

 Ahora en el constructor generaremos un valor aleatorio entre 0 y 5000 y lo guardamos en iSleepTime.

thread6

Definimos un método que será el que se llame para cada hilo multitarea:

thread7

Ahora que ya hemos definido la clase y su método que será ejecutado de forma concurrente vamos a ver las llamadas para crear los 3 subprocesos desde el Main.

Creación de Thread-s

thread8

Código completo:

</p>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Threading;

namespace MsgPrinterThead
{
/**
* Clase clsMsgPrinterThead
* El constructor genera un número de forma aleatoria que será usado como tiempo de inactividad del
* hilo cuando pausemos su ejecución con el método...
*/
class clsMsgPrinterThead
{
private int iSleepTime; //< Timepo inactividad cuando se pause ejecución.
private static Random myRandom = new Random(); //< Clase para genear números aleatorios.

public clsMsgPrinterThead()
{
//Tiempo de inactividad aleatorio entre 0 y 5s (5000 milisegundos)
iSleepTime = myRandom.Next(5001);
}

public void Print()
{
Thread actual = Thread.CurrentThread;
Console.WriteLine("{0} va a estar inactivo {1} miligsegundos",
actual.Name,this.iSleepTime);
Thread.Sleep(this.iSleepTime);
Console.WriteLine("{0} dejo de estar inactivo.",actual.Name);
}
}

class Program
{
static void Main(string[] args)
{
clsMsgPrinterThead Printer1 = new clsMsgPrinterThead();
Thread subproceso1 = new Thread(new ThreadStart(Printer1.Print));
subproceso1.Name = "subproceso1";

clsMsgPrinterThead Printer2 = new clsMsgPrinterThead();
Thread subproceso2 = new Thread(new ThreadStart(Printer2.Print));
subproceso2.Name = "subproceso2";

clsMsgPrinterThead Printer3 = new clsMsgPrinterThead();
Thread subproceso3 = new Thread(new ThreadStart(Printer3.Print));
subproceso3.Name = "subproceso3";

subproceso1.Start();
subproceso1.Abort();

subproceso2.Start();
subproceso3.Start();
}
}
}
<p style="text-align: justify;">

C# PruebaThis: Referencia a los miembros del objeto actual con “this”

Los objetos pueden acceder a una referencia a si mismo usando la palabra clave this. Hace referencia a la instancia actual de la clase. Puede ser usado con el fin de evitar ambigüedades como en este ejemplo:

t1

El constructor de la clase TiempoSimple recibe tres argumentos int para inicializar el objeto. En el constructor usamos nombres de parámetros de entrada idénticos a los nombres de las variables de instancia de la clase (hora,minuto,segundo). Usando this podemos diferenciar entre unas y otras.

El método público y accesible por el cliente  CrearString devuelve un objeto string creado usando la referencias this de forma explícita e implícita.

t2

Ahora vemos como podemos usar la clase, creamos una instancia de la clase  TiempoSimple y invocamos a su constructor.  Luego invocamos al método CrearString del objeto y muestra los resultados en pantalla.

t3

Ejemplo completo:


 /*
  * Created by SharpDevelop.
  * User: i.landajuela
  * Date: 20/12/2016
  * Time: 17:15
  * 
  * To change this template use Tools | Options | Coding | Edit Standard Headers.
  */
 using System;
 
 namespace PruebaThis
 {
     
     public class TiempoSimple
     {
         private int hora=0,minuto=0,segundo=0;
         
         public TiempoSimple(int hora,int minuto, int segundo)
         {
             this.hora = hora;
             this.minuto = minuto;
             this.segundo = segundo;
         }
         
         public string CrearString()
         {
             return string.Format("{0:24}: {1}\n{2,24}: {3}",
                                  "this.AStringUniversal()",this.AStringUniversal(),
                                  "AStringUniversal()",AStringUniversal());
         }
         
         public string AStringUniversal()
         {
             return string.Format("{0:D2}:{1:D2}:{2:D2}",
                                 this.hora,this.minuto,this.segundo);
         }
 
     }
     
     class Program
     {
         public static void Main(string[] args)
         {
             TiempoSimple objT1 = new TiempoSimple(23,24,12);
             
             Console.WriteLine(objT1.CrearString());
                         
             Console.Write("Press any key to continue . . . ");
             Console.ReadKey(true);
         }
     }
 }