C# Multithreading

ThreadUn thread est défini comme le chemin d'exécution d'un programme. Chaque thread définit un flux de contrôle unique. Si votre application implique des opérations complexes et chronophages, il est souvent bénéfique de définir différents chemins d'exécution de threads, chaque thread effectuant un travail spécifique.

Thread estProcessus légerUn exemple courant d'utilisation de threads est la mise en œuvre de la programmation parallèle dans les systèmes d'exploitation modernes. L'utilisation de threads évite le gaspillage de cycles CPU et améliore l'efficacité des applications.

Les programmes que nous avons écrits jusqu'à présent s'exécutent comme un processus unique avec un seul thread comme instance d'exécution de l'application. Cependant, de cette manière, l'application ne peut exécuter qu'une seule tâche à la fois. Pour exécuter plusieurs tâches simultanément, elle peut être divisée en threads plus petits.

Cycle de vie d'un thread

Le cycle de vie d'un thread commence lorsqu'un objet de la classe System.Threading.Thread est créé, et se termine lorsque le thread est terminé ou termine son exécution.

Les différents états du cycle de vie d'un thread sont listés ci-dessous :

  • État non démarré: État lorsqu'une instance de thread a été créée mais que la méthode Start n'a pas été appelée.
  • État prêt: État lorsqu'un thread est prêt à s'exécuter et attend un cycle CPU.
  • État non exécutable: Un thread n'est pas exécutable dans les cas suivants :
    • La méthode Sleep a été appelée
    • La méthode Wait a été appelée
    • Bloqué par une opération d'E/S
  • État mort: État lorsque le thread a terminé son exécution ou a été avorté.

thread principal

En C#,System.Threading.ThreadLa classe Thread est utilisée pour le travail sur les threads. Elle permet de créer et d'accéder à des threads individuels dans une application multithread. Le premier thread exécuté dans un processus est appelé thread principal.thread principal。

Lorsqu'un programme C# commence son exécution, le thread principal est automatiquement créé. En utilisantThread, les threads créés par la classe sont appelés threads enfants du thread principal. Vous pouvez utiliser la propriétéCurrentThreadde la classe Thread pour accéder au thread.

Le programme suivant démontre l'exécution du thread principal :

Exemple

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class MainThreadProgram
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "MainThread";
            Console.WriteLine("This is {0}", th.Name);
            Console.ReadKey();
        }
    }
}

Lorsque le code ci-dessus est compilé et exécuté, il produit les résultats suivants :

This is MainThread

Propriétés et méthodes courantes de la classe Thread

Le tableau ci-dessous répertorieThreadquelques méthodes courantes de la classePropriété:

PropriétéDescription
CurrentContextObtient le contexte actuel dans lequel le thread s'exécute.
CurrentCultureObtient ou définit la culture du thread actuel.
CurrentPrincipalObtient ou définit le principal actuel du thread (pour la sécurité basée sur les rôles).
CurrentThreadObtient le thread en cours d'exécution.
CurrentUICultureObtient ou définit la culture actuelle utilisée par le gestionnaire de ressources pour rechercher des ressources spécifiques à la culture au moment de l'exécution.
ExecutionContextObtient un objet ExecutionContext qui contient des informations contextuelles sur le thread actuel.
IsAliveObtient une valeur qui indique l'état d'exécution du thread actuel.
IsBackgroundObtient ou définit une valeur indiquant si un thread est un thread d'arrière-plan.
IsThreadPoolThreadObtient une valeur indiquant si un thread appartient au pool de threads managé.
ManagedThreadIdObtient l'identificateur unique du thread managé actuel.
NameObtient ou définit le nom du thread.
PrioritéObtient ou définit une valeur indiquant la priorité de planification du thread.
ThreadStateObtient une valeur qui contient l'état du thread actuel.

Le tableau ci-dessous répertorieThreadquelques méthodes courantes de la classeMéthodes:

Numéro de sérieNom de la méthode et description
1public void Abort()
Lève une ThreadAbortException sur le thread sur lequel cette méthode est appelée, afin de commencer le processus de terminaison de ce thread. L'appel de cette méthode termine généralement le thread.
2public static LocalDataStoreSlot AllocateDataSlot()
Alloue un emplacement de données sans nom sur tous les threads. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
Alloue un emplacement de données nommé sur tous les threads. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
4public static void BeginCriticalRegion()
Signale à l'hôte que l'exécution est sur le point d'entrer dans une région de code où les effets des abandons de threads ou des exceptions non gérées peuvent compromettre d'autres tâches dans le domaine d'application.
5public static void BeginThreadAffinity()
Signale à l'hôte que du code managé est sur le point d'exécuter des instructions qui dépendent de l'identité du thread de système d'exploitation physique actuel.
6public static void EndCriticalRegion()
Signale à l'hôte que l'exécution est sur le point d'entrer dans une région de code dans laquelle les abandons de threads ou les exceptions non gérées affectent uniquement la tâche actuelle.
7public static void EndThreadAffinity()
Notifie l'hôte que le code managé a terminé l'exécution d'instructions qui dépendent de l'identité du thread de système d'exploitation physique actuel.
8public static void FreeNamedDataSlot(string name)
Élimine l'association entre les noms et les emplacements pour tous les threads du processus. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
9public static Object GetData( LocalDataStoreSlot slot )
Récupère la valeur à partir de l'emplacement spécifié sur le thread actuel, dans le domaine actuel du thread actuel. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
10public static AppDomain GetDomain()
Renvoie le domaine actuel dans lequel le thread actuel s'exécute.
11public static AppDomain GetDomainID()
Renvoie un identificateur unique de domaine d'application.
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
Recherche un emplacement de données nommé. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
13public void Interrupt()
Interrompt un thread qui se trouve dans l'état de thread WaitSleepJoin.
14public void Join()
Bloque le thread appelant jusqu'à ce qu'un thread se termine, tout en continuant à effectuer le pompage de messages COM et SendMessage standard. Cette méthode a différentes formes de surcharge.
15public static void MemoryBarrier()
Synchronise l'accès à la mémoire comme suit : le processeur exécutant le thread actuel ne peut pas réorganiser les instructions de telle sorte que les accès mémoire après l'appel à MemoryBarrier s'exécutent avant les accès mémoire antérieurs à l'appel à MemoryBarrier.
16public static void ResetAbort()
Annule l'Abort demandé pour le thread actuel.
17public static void SetData( LocalDataStoreSlot slot, Object data )
Définit des données dans l'emplacement spécifié sur le thread en cours d'exécution pour le domaine actuel du thread. Pour de meilleures performances, utilisez plutôt des champs marqués avec l'attribut ThreadStaticAttribute.
18public void Start()
Démarre un thread.
19public static void Sleep( int millisecondsTimeout )
Suspend le thread pendant une période de temps.
20public static void SpinWait( int iterations )
Fait attendre un thread pendant la durée définie par le paramètre iterations.
21public static byte VolatileRead( ref byte address )
public static double VolatileRead( ref double address )
public static int VolatileRead( ref int address )
public static Object VolatileRead( ref Object address )

Lit la valeur du champ. Quel que soit le nombre de processeurs ou l'état du cache du processeur, la valeur est la dernière valeur écrite par n'importe quel processeur de l'ordinateur. Cette méthode a différentes formes de surcharge. Seules certaines formes sont données ici.
22public static void VolatileWrite( ref byte address, byte value )
public static void VolatileWrite( ref double address, double value )
public static void VolatileWrite( ref int address, int value )
public static void VolatileWrite( ref Object address, Object value )

Écrit immédiatement une valeur dans un champ afin que la valeur soit visible par tous les processeurs de l'ordinateur. Cette méthode a différentes formes de surcharge. Seules quelques formes sont données ici.
23public static bool Yield()
Amène le thread appelant à exécuter un autre thread qui est prêt à s'exécuter sur le processeur actuel. Le thread à exécuter est sélectionné par le système d'exploitation.

Création de threads

Les threads sont créés en étendant la classe Thread. La classe Thread étendue appelle la méthodeStart()pour démarrer l'exécution du thread enfant.

Le programme suivant démontre ce concept :

Exemple

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

Lorsque le code ci-dessus est compilé et exécuté, il produit les résultats suivants :

In Main: Creating the Child thread
Child thread starts

Gérer les threads

La classe Thread fournit diverses méthodes pour gérer les threads.

L'exemple suivant illustresleep()Utilisation de la méthode pour suspendre un thread pendant une durée spécifiée.

Exemple

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            // Mettre le thread en pause pendant 5000 millisecondes
            int sleepfor = 5000;
            Console.WriteLine("Child Thread Paused for {0} seconds",
                              sleepfor / 1000);
            Thread.Sleep(sleepfor);
            Console.WriteLine("Child thread resumes");
        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

Lorsque le code ci-dessus est compilé et exécuté, il produit les résultats suivants :

In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes

Destruction des threads

Abort()est une méthode utilisée pour détruire le thread.

En lançantthreadabortexceptionInterrompt le thread à l'exécution. Cette exception ne peut pas être interceptée. S'il y a unfinallybloc, le contrôle est transféré àfinallyBloc.

Le programme suivant illustre ceci :

Exemple

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {

                Console.WriteLine("Child thread starts");
                // Compter jusqu'à 10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Thread enfant terminé");

            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Exception d'abandon de thread");
            }
            finally
            {
                Console.WriteLine("Impossible d'intercepter l'exception de thread");
            }

        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // Mettre en pause le thread principal pendant un certain temps
            Thread.Sleep(2000);
            // Maintenant, abandonner le thread enfant
            Console.WriteLine("Dans Main : abandon du thread enfant");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

Lorsque le code ci-dessus est compilé et exécuté, il produit les résultats suivants :

In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception 
autres extensions