web-dev-qa-db-fra.com

Meilleure façon de définir, démarrer et arrêter les threads en C #

Je fais un petit programme, qui a peu de threads, qui tourne constamment. À un moment donné, je veux peut-être arrêter l'un d'eux, puis, après une période de temps aléatoire, le redémarrer. Alors d'abord, quelle est la meilleure façon de définir un Thread, voici mon code

private void startCheckCommandThread() {
        isRunning = true;
        //commandThread = new Thread(checkForCommand);
        //commandThread.IsBackground = true;
        //commandThread.Start();

        new Thread(() => {
            Thread.CurrentThread.IsBackground = true;
            checkForCommand();
        }).Start();
    }

La manière commentée, ou l'autre? Et voici comment je les arrête et les redémarre:

// get condition variable
if (condition) {
    isRunning = false;
    // here it must be 100% guaranteed that the Thread will stop
} else {
    startCheckCommandThread();
}

Alors, quelle est la meilleure façon de le faire, la plupart du temps je pose des questions sur la stabilité (c'est-à-dire à l'épreuve des erreurs) et les performances maximales (aussi faible utilisation du processeur que possible et moins d'allocation de mémoire que possible)?

6
Skaidar

Je fais un petit programme, qui a peu de threads, qui tourne constamment. À un moment donné, je peux vouloir arrêter l'un d'eux, puis, après une période de temps aléatoire, le redémarrer. Quelle est la meilleure façon de créer un fil?

La meilleure façon est de ne pas le faire du tout. Si j'ai du travail à faire en parallèle avec un autre programme, la meilleure pratique est de démarrer un autre processus. Si j'ai du travail à faire de façon asynchrone, j'utilise Task Parallel Library et je le laisse gérer mes threads.

Les threads sont simplement la mauvaise unité de granularité pour la plupart des tâches. Les threads sont travailleurs; plutôt que de gérer travailleurs, au lieu de gérer tâches; laissez le TPL déterminer combien de travailleurs sont nécessaires.

la plupart du temps je pose des questions sur la stabilité

Il est notoirement difficile d’envoyer du code de manière appropriée. Vous devez vous soucier de la cohérence, des barrières mémoire, des lectures et écritures non atomiques, des blocages, des livelocks, ... C'est pourquoi l'utilisation de processus est bien meilleure.

Si vous devez tout garder en un seul processus, utilisez l'outil niveau le plus élevé disponible. Encore une fois, n'essayez pas de manipuler directement les threads. Si vous avez un travail asynchrone à faire, utilisez le TPL. Si vous devez annuler une partie de ce travail, utilisez des jetons d'annulation. N'essayez pas de contrôler directement les threads; vous vous tromperez.

Si pour une raison quelconque vous ne voulez pas utiliser le TPL et que vous voulez utiliser directement les threads, alors créez un schéma par lequel les threads communiquent sur des canaux bien définis et soigneusement testés et ayez le code révisé par des experts. Vous ne voulez jamais abandonner un thread sauf en cas d'urgence; cela peut corrompre vos structures de données de manière inhabituelle. Si vous avez besoin d'un thread pour arrêter, vous voulez avoir un système soigneusement conçu et soigneusement implémenté qui permet à un thread de signaler clairement à un autre thread qu'il est temps de l'arrêter, et de le faire arrêter proprement.

et des performances maximales (utilisation du processeur aussi faible que possible et moins d'allocation de mémoire que possible)?

Je ne comprends jamais pourquoi les gens disent "Je veux utiliser des threads pour maintenir une faible utilisation de mon processeur". Les threads sont là pour maintenir l'utilisation de votre CPU élevé. Vous avez payé beaucoup d'argent pour ces processeurs; Utilise les! La situation idéale est d'avoir tous les CPU de votre machine fonctionnant à 100% tout le temps faisant un travail utile. Vous ne diriez pas que votre objectif était de construire une usine où les travailleurs sont inactifs 95% du temps, alors pourquoi feriez-vous de même pour un programme?

Les threads maintiennent une utilisation élevée de votre processeur, car si vous êtes intelligent, vous affecterez un thread de travail lié au processeur à chaque processeur et le ferez rien d'autre autre que de travailler sur le problème vous lancez dessus. Si vous avez plus de travail lié au CPU que de CPU, et plus de threads pour le gérer, alors les CPU vont basculer entre les threads et tout devient moins efficace.

Si vous voulez éviter l'allocation de mémoire, gardez certainement votre nombre de threads aussi bas que possible. Les threads représentent la ressource la plus coûteuse en mémoire que vous puissiez éventuellement allouer. Par défaut, chacun consomme un million d'octets d'espace d'adressage pour la pile.

29
Eric Lippert

En ce qui concerne les deux façons de démarrer le fil, elles sont à peu près équivalentes, il s'agit donc de savoir si vous préférez un lambda.

Pour arrêter le thread, le moyen le plus sûr est de vérifier une valeur de champ pour une modification. Cela peut être fait en utilisant une barrière de mémoire, par exemple, en utilisant une instruction de verrouillage ou un moniteur fournira automatiquement une barrière de mémoire. La manière la plus simple de synchroniser les threads est d'utiliser une instruction de verrouillage autour de tous les accès et modifications d'un champ booléen. Le champ serait contenu dans un objet associé au thread à arrêter.

Lorsque la variable change, vous quittez le thread en revenant de la méthode de niveau supérieur qui s'exécute dans le thread.

La plupart des développeurs ne "réutilisent" pas un thread. Une fois que vous le quittez, il est parti - vous devez en créer un nouveau. Vous pouvez réutiliser l'objet associé au thread. Bien que vous puissiez conserver le fil, le suspendre jusqu'à ce que vous en ayez besoin, cela est généralement considéré comme une mauvaise conception, car le fil gaspille des ressources pendant sa suspension. De plus, il doit bloquer de manière à ne pas brûler les cycles en attendant.

Si vous êtes intéressé par des performances maximales, vous souhaiterez peut-être regarder la synchronisation en utilisant Interlocked. C'est plus rapide, mais difficile à utiliser. Ne l'utilisez pas si vous débutez avec le multithreading. Je recommande ce site Web pour les didacticiels de threading: Threading in C #

4
Frank Hileman

La meilleure façon de gérer les threads est de ne pas le faire.

L'utilisation du filetage n'est pas un objectif, c'est un moyen pour une fin, à savoir. simultanéité. En C #, nous obtenons la simultanéité sans gestion directe des threads grâce à des expressions lambda asynchrones/attendues, ou parfois (comme mentionné par d'autres), le plus souvent via PLINQ.

Il est coûteux, lent et sous réserve de disponibilité de démarrer et d'arrêter les threads. Au lieu de cela, les approches ci-dessus utilisent un pool de threads préalloués. Les ressources sont rassemblées lors de la configuration et de la destruction du processus par du code écrit par des personnes ayant une expérience significative de la gestion des threads.

Ils ont également accès aux développeurs de la plateforme et aux équipes de test à grande échelle qui ont également une expérience appropriée. Il est peu probable qu'une équipe de développement d'applications puisse égaler cela.

2
Peter Wone