Introducció

La multifil i la programació paral·lela són conceptes avançats que permeten als programes executar múltiples tasques simultàniament, millorant així el rendiment i l'eficiència. En aquest tema, explorarem com utilitzar fils (threads) i tasques (tasks) en C# per aconseguir una execució concurrent i paral·lela.

Objectius

  • Comprendre els conceptes bàsics de la multifil i la programació paral·lela.
  • Aprendre a crear i gestionar fils en C#.
  • Utilitzar la biblioteca de tasques paral·leles (TPL) per a la programació paral·lela.
  • Implementar patrons comuns de programació concurrent.

Conceptes Clau

  1. Fils (Threads)

Un fil és la unitat bàsica d'execució en un programa. Cada fil pot executar una tasca independentment dels altres fils.

  1. Programació Paral·lela

La programació paral·lela implica dividir una tasca en sub-tasques que es poden executar simultàniament en múltiples fils o processadors.

  1. Biblioteca de Tasques Paral·leles (TPL)

La TPL és una biblioteca de .NET que facilita la creació i gestió de tasques paral·leles.

Creació i Gestió de Fils

Exemple Bàsic de Fils

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(new ThreadStart(DoWork));
        thread.Start();
        thread.Join(); // Espera que el fil acabi
        Console.WriteLine("Fil principal acabat.");
    }

    static void DoWork()
    {
        Console.WriteLine("Treballant en un fil separat.");
    }
}

Explicació:

  • Thread thread = new Thread(new ThreadStart(DoWork));: Crea un nou fil que executarà el mètode DoWork.
  • thread.Start();: Inicia l'execució del fil.
  • thread.Join();: Espera que el fil acabi abans de continuar amb el fil principal.

Sincronització de Fils

Quan múltiples fils accedeixen a recursos compartits, és important sincronitzar-los per evitar condicions de carrera.

Exemple amb 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($"Counter: {_counter}");
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            lock (_lock)
            {
                _counter++;
            }
        }
    }
}

Explicació:

  • lock (_lock): Assegura que només un fil pugui accedir al bloc de codi protegit al mateix temps.

Biblioteca de Tasques Paral·leles (TPL)

Exemple Bàsic amb Task

using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Task task = Task.Run(() => DoWork());
        task.Wait(); // Espera que la tasca acabi
        Console.WriteLine("Fil principal acabat.");
    }

    static void DoWork()
    {
        Console.WriteLine("Treballant en una tasca separada.");
    }
}

Explicació:

  • Task task = Task.Run(() => DoWork());: Crea i inicia una nova tasca que executarà el mètode DoWork.
  • task.Wait();: Espera que la tasca acabi abans de continuar amb el fil principal.

Paral·lelisme amb Parallel.For

using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Parallel.For(0, 10, i =>
        {
            Console.WriteLine($"Treballant en l'índex {i}");
        });
    }
}

Explicació:

  • Parallel.For(0, 10, i => { ... });: Executa el bloc de codi per a cada valor de i en paral·lel.

Exercicis Pràctics

Exercici 1: Crear i Gestionar Fils

Descripció:

Crea un programa que iniciï dos fils separats. Cada fil ha d'incrementar un comptador compartit 1000 vegades. Utilitza lock per sincronitzar l'accés al comptador.

Solució:

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($"Counter: {_counter}");
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            lock (_lock)
            {
                _counter++;
            }
        }
    }
}

Exercici 2: Utilitzar Task per a la Programació Paral·lela

Descripció:

Crea un programa que utilitzi Task per executar tres tasques paral·leles. Cada tasca ha de mostrar un missatge diferent.

Solució:

using System;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        Task task1 = Task.Run(() => Console.WriteLine("Tasca 1"));
        Task task2 = Task.Run(() => Console.WriteLine("Tasca 2"));
        Task task3 = Task.Run(() => Console.WriteLine("Tasca 3"));

        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("Totes les tasques han acabat.");
    }
}

Resum

En aquesta secció, hem après els conceptes bàsics de la multifil i la programació paral·lela en C#. Hem vist com crear i gestionar fils, com sincronitzar-los per evitar condicions de carrera, i com utilitzar la biblioteca de tasques paral·leles (TPL) per a la programació paral·lela. A més, hem practicat aquests conceptes amb exercicis pràctics.

En el següent mòdul, explorarem altres conceptes avançats de C#, com la reflexió i els atributs.

© Copyright 2024. Tots els drets reservats