¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. C# 10
  3. Multi
Extrait - C# 10 Desarrolle aplicaciones Windows con Visual Studio 2022
Extractos del libro
C# 10 Desarrolle aplicaciones Windows con Visual Studio 2022
4 opiniones
Volver a la página de compra del libro

Multi-threading

Introducción

La programación multi-thread implica el reparto de tareas (thread) de una misma aplicación, de forma que se realicen de manera independiente unas de otras. De este modo se consigue un uso óptimo del tiempo del procesador. Las tareas no se realizan en realidad de forma paralela. El procesador asigna un tiempo de procesamiento a cada tarea en función de su importancia. Este cambio de contexto implica que el procesador memoriza la pila de la tarea actual antes de restaurar la de la tarea a la que le da paso. C# 5 introduce de nuevo palabras clave (await y async) para facilitar el desarrollo asíncrono. El desarrollo síncrono implica que una función que se invoca bloquea la ejecución del programa hasta que aquélla termina. Cuando una función se invoca de forma asíncrona, la ejecución del programa principal continúa. Por tanto, existe una noción de ejecución en paralelo y de concurrencia, como ocurre con la programación multi-thread.

Los ejemplos de este capítulo están disponibles en el código fuente del proyecto MultiThreading de la solución.

La clase Thread

El espacio de nombres System.Threading alberga clases que permiten crear y controlar las tareas, principalmente mediante la clase Thread. Los threads se deben usar cuando una aplicación debe gestionar varias tareas independientes, como gestionar una interfaz de usuario o realizar un tratamiento de datos. La aplicación tendrá mejor rendimiento si estas dos tareas se desarrollan en threads específicos.

1. Crear un thread

El delegado ThreadStart se emplea para crear un nuevo thread. Su constructor toma como argumento la declaración del método que se ejecutará en este nuevo thread:

ThreadStart newThread = new ThreadStart(OtherThread); 

El delegado se pasa a continuación como argumento del constructor del objeto Thread:

Thread thread = new Thread(newThread); 

A continuación, se llama al método Start del objeto Thread para iniciar la llamada al método del delegado ThreadStart:

thread.Start(); 

El ejemplo completo en forma de aplicación de consola es el siguiente:

static void Main(string[] args)  
{  
    Console.WriteLine("Inicio del thread principal");  
    ThreadStart newThread = new ThreadStart(OtherThread);  
    Thread thread = new Thread(newThread);  
    thread.Start();  
    Console.WriteLine("Thread principal terminado");  
}  
public static void OtherThread()  
{  
    Console.WriteLine("Inicio del thread secundario");  
  
    Console.WriteLine("Thread secundario terminado");  
} 

Cuando inicie esta aplicación ([F5]), la consola mostrará el resultado siguiente: 

Inicio del thread principal  
Thread principal terminado  
Inicio del thread secundario 
Thread secundario terminado 

Puede observar que el método Main se ejecuta completamente antes de que se haya iniciado el método OtherThread. Esto provoca que los dos métodos se ejecuten de manera asíncrona y, por tanto, en threads diferentes.

La propiedad estática CurrentThread de la clase Thread le permite acceder al thread actual en el contexto de ejecución. En el ejemplo anterior, esto significa que la propiedad CurrentThread no tendrá el mismo valor según...

Funciones asíncronas

La versión 5 del lenguaje C# introdujo las nuevas palabras clave async y await que sirven para facilitar la implementación de funciones asíncronas de manera que se parezca a la implementación de un método síncrono.

A continuación, se muestra el ejemplo de una función que necesita una cantidad de tiempo importante para ejecutarse:

public static double TimeConsumingFunction()  
{  
        double x = 1;  
        for (int i = 1; i < 100000000; i++)  
        {  
                   x += Math.Tan(x) / i;  
        }  
        return x;  
} 

Esta función bloquea la ejecución del programa hasta que termina y devuelve su resultado a la llamada:

Console.WriteLine("Ejecución de una función larga");  
  
DateTime inicio = DateTime.Now;  
double resultado = TimeConsumingFunction();  
DateTime fin = DateTime.Now;  
  
Console.Write("Resultado: ");  
Console.WriteLine(resultado);  
  
Console.Write("Tiempo de ejecución: ");  
Console.WriteLine(TimeSpan.FromTicks(fin.Ticks - inicio.Ticks). 
TotalSeconds); 
images/cap26_img_01.png

1. Task y Task<TResult>

El espacio de nombres System.Threading.Tasks define una clase Task y una clase Task<TResult> que representan un método que se completará...

El componente BackgroundWorker

El componente BackgroundWorker se utiliza en el formulario Send de la solución de ejemplo.

La caja de herramientas de Visual Studio proporciona el componente BackgroundWorker. Resulta muy útil para las aplicaciones de Windows que deben gestionar una interfaz de usuario y, al mismo tiempo, se tienen que refrescar para poder realizar de esta manera, las operaciones complicadas.

images/cap26_img_04.png

Como indica la interfaz, el componente BackgroundWorker permite ejecutar una operación en un thread separado. Basta con instanciar un nuevo objeto BackgroundWorker desde el diseñador de pantallas o mediante código e indicarle el método a ejecutar en un thread separado:

BackgroundWorker bw = new BackgroundWorker();  
bw.DoWork += new DoWorkEventHandler(bw_DoWork); 

Cuando desee iniciar la ejecución de las tareas en segundo plano, llame al método RunWorkerAsync del objeto BackgroundWorker:

bw.RunWorkerAsync(); 

El componente se ha simplificado al máximo para facilitar su uso. Contiene dos propiedades, WorkerReportsProgress y WorkerSupportsCancellation, que permiten respectivamente especificar si el componente indica su progresión a los objetos suscritos a su evento ProgressChanged y si la ejecución de la tarea desatendida se puede anular mediante el método CancelAsync:

bw.WorkerReportsProgress = true;  
bw.WorkerSupportsCancellation = true; 

El componente también...