La programación multihilo y paralela es una técnica avanzada que permite a los programas ejecutar múltiples tareas simultáneamente, mejorando el rendimiento y la eficiencia. En este módulo, aprenderemos los conceptos básicos y avanzados de la programación multihilo y paralela en C#.
Contenidos
- Introducción al Multihilo
- Creación y Gestión de Hilos
- Sincronización de Hilos
- Programación Paralela con TPL (Task Parallel Library)
- PLINQ (Parallel LINQ)
- Ejercicios Prácticos
- Introducción al Multihilo
El multihilo permite que un programa realice múltiples operaciones al mismo tiempo. Cada hilo puede ejecutar una tarea diferente, lo que puede mejorar significativamente el rendimiento de aplicaciones que realizan operaciones intensivas en CPU o I/O.
Conceptos Clave
- Hilo (Thread): La unidad más pequeña de procesamiento que puede ser programada por un sistema operativo.
- Proceso: Un programa en ejecución que puede contener uno o más hilos.
- Sincronización: Mecanismos para coordinar la ejecución de hilos y evitar condiciones de carrera.
- Creación y Gestión de Hilos
En C#, los hilos se pueden crear y gestionar utilizando la clase Thread
del espacio de nombres System.Threading
.
Ejemplo Básico
using System; using System.Threading; class Program { static void Main() { Thread thread = new Thread(new ThreadStart(DoWork)); thread.Start(); thread.Join(); // Espera a que el hilo termine Console.WriteLine("Hilo principal terminado."); } static void DoWork() { Console.WriteLine("Hilo secundario en ejecución."); } }
Explicación
- ThreadStart: Delegado que representa el método que se ejecutará en el hilo.
- Start(): Inicia la ejecución del hilo.
- Join(): Bloquea el hilo principal hasta que el hilo secundario termine.
- Sincronización de Hilos
La sincronización es crucial para evitar problemas como condiciones de carrera y asegurar que los recursos compartidos se manejen correctamente.
Bloqueo con lock
using System; using System.Threading; class Program { private static readonly object _lock = new object(); private static int _counter = 0; static void Main() { Thread thread1 = new Thread(IncrementCounter); Thread thread2 = new Thread(IncrementCounter); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine($"Contador final: {_counter}"); } static void IncrementCounter() { for (int i = 0; i < 1000; i++) { lock (_lock) { _counter++; } } } }
Explicación
- lock: Asegura que solo un hilo pueda acceder a la sección crítica del código a la vez.
- _lock: Objeto utilizado para bloquear la sección crítica.
- Programación Paralela con TPL (Task Parallel Library)
La TPL simplifica la creación y gestión de tareas paralelas.
Ejemplo Básico con Task
using System; using System.Threading.Tasks; class Program { static void Main() { Task task = Task.Run(() => DoWork()); task.Wait(); // Espera a que la tarea termine Console.WriteLine("Tarea completada."); } static void DoWork() { Console.WriteLine("Tarea en ejecución."); } }
Explicación
- Task.Run: Crea y ejecuta una nueva tarea.
- Wait(): Bloquea el hilo principal hasta que la tarea termine.
- PLINQ (Parallel LINQ)
PLINQ permite realizar consultas LINQ en paralelo, aprovechando múltiples núcleos de CPU.
Ejemplo Básico
using System; using System.Linq; class Program { static void Main() { var numbers = Enumerable.Range(1, 1000000); var evenNumbers = numbers.AsParallel().Where(n => n % 2 == 0).ToArray(); Console.WriteLine($"Números pares encontrados: {evenNumbers.Length}"); } }
Explicación
- AsParallel(): Convierte una secuencia en una consulta paralela.
- Where: Filtra los elementos de la secuencia.
- Ejercicios Prácticos
Ejercicio 1: Crear y Gestionar Hilos
Objetivo: Crear dos hilos que incrementen un contador compartido de manera segura.
Código Inicial:
using System; using System.Threading; class Program { private static int _counter = 0; static void Main() { // Crear y empezar hilos aquí } static void IncrementCounter() { for (int i = 0; i < 1000; i++) { // Incrementar contador aquí } } }
Solución:
using System; using System.Threading; class Program { private static readonly object _lock = new object(); private static int _counter = 0; static void Main() { Thread thread1 = new Thread(IncrementCounter); Thread thread2 = new Thread(IncrementCounter); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine($"Contador final: {_counter}"); } static void IncrementCounter() { for (int i = 0; i < 1000; i++) { lock (_lock) { _counter++; } } } }
Ejercicio 2: Uso de TPL
Objetivo: Crear una tarea que calcule la suma de los números del 1 al 1000.
Código Inicial:
using System; using System.Threading.Tasks; class Program { static void Main() { // Crear y ejecutar tarea aquí } static void CalculateSum() { // Calcular suma aquí } }
Solución:
using System; using System.Threading.Tasks; class Program { static void Main() { Task task = Task.Run(() => CalculateSum()); task.Wait(); Console.WriteLine("Tarea completada."); } static void CalculateSum() { int sum = 0; for (int i = 1; i <= 1000; i++) { sum += i; } Console.WriteLine($"Suma: {sum}"); } }
Conclusión
En este módulo, hemos cubierto los conceptos fundamentales y avanzados de la programación multihilo y paralela en C#. Aprendimos a crear y gestionar hilos, sincronizar hilos para evitar condiciones de carrera, y utilizar la TPL y PLINQ para realizar tareas en paralelo de manera eficiente. Estos conocimientos son esenciales para desarrollar aplicaciones que requieran un alto rendimiento y eficiencia.
En el próximo módulo, exploraremos la entrada/salida de archivos en C#, lo que nos permitirá manejar datos de manera persistente.
Curso de Programación en C#
Módulo 1: Introducción a C#
- Introducción a C#
- Configuración del Entorno de Desarrollo
- Programa Hola Mundo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
Módulo 2: Estructuras de Control
Módulo 3: Programación Orientada a Objetos
- Clases y Objetos
- Métodos
- Constructores y Destructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
Módulo 4: Conceptos Avanzados de C#
- Interfaces
- Delegados y Eventos
- Genéricos
- Colecciones
- LINQ (Consulta Integrada en el Lenguaje)
- Programación Asíncrona
Módulo 5: Trabajando con Datos
Módulo 6: Temas Avanzados
- Reflexión
- Atributos
- Programación Dinámica
- Gestión de Memoria y Recolección de Basura
- Multihilo y Programación Paralela
Módulo 7: Construcción de Aplicaciones
Módulo 8: Mejores Prácticas y Patrones de Diseño
- Estándares de Codificación y Mejores Prácticas
- Patrones de Diseño
- Pruebas Unitarias
- Revisión y Refactorización de Código