Existe-t-il des cas où il est préférable d'utiliser un ancien objet Thread au lieu de l'une des constructions les plus récentes?

112

Je vois beaucoup de gens dans les articles de blog et ici sur SO évitant ou déconseillant l'utilisation de la Threadclasse dans les versions récentes de C # (et je veux dire bien sûr 4.0+, avec l'ajout de Task& friends). Même avant, il y avait des débats sur le fait que la fonctionnalité d'un ancien thread peut être remplacée dans de nombreux cas par la ThreadPoolclasse.

En outre, d'autres mécanismes spécialisés rendent la Threadclasse moins attrayante, comme le Timerremplacement du combo laid Thread+ Sleep, tandis que pour les interfaces graphiques que nous avons BackgroundWorker, etc.

Pourtant, le Threadsemble rester un concept très familier pour certaines personnes (moi y compris), des personnes qui, confrontées à une tâche qui implique une sorte d'exécution parallèle, se lancent directement dans l'utilisation de la bonne vieille Threadclasse. Je me suis demandé récemment s'il était temps de modifier mes habitudes.

Ma question est donc la suivante: y a-t-il des cas où il est nécessaire ou utile d'utiliser un vieil Threadobjet ordinaire au lieu de l'une des constructions ci-dessus?

Tudor
la source
4
Pas pour pinailler (ou peut-être ... :)), mais c'est .NET (c'est-à-dire pas limité à C #).
MetalMikester
11
Jusqu'à présent, le représentant moyen d'un utilisateur qui répond à cette question est 64.5k. Un spectacle assez impressionnant
zzzzBov
1
@zzzzBov: Je pensais publier ma propre réponse mais maintenant je ne peux pas de peur de faire baisser la moyenne :)
Brian Gideon
@BrianGideon, je ne vois aucune raison pour laquelle cela devrait vous empêcher, vous ou quelqu'un d'autre, de répondre à cette question.
zzzzBov
@Brian Gideon: Par tous les moyens, veuillez poster. :)
Tudor

Réponses:

107

La classe Thread ne peut pas être rendue obsolète car il s'agit évidemment d'un détail d'implémentation de tous les autres modèles que vous mentionnez.

Mais ce n'est pas vraiment votre question; votre question est

existe-t-il des cas où il est nécessaire ou utile d'utiliser un ancien objet Thread au lieu de l'une des constructions ci-dessus?

Sûr. Dans les cas précis où l'une des constructions de niveau supérieur ne répond pas à vos besoins.

Mon conseil est que si vous vous trouvez dans une situation où les outils d'abstraction supérieure existants ne répondent pas à vos besoins et que vous souhaitez implémenter une solution à l'aide de threads, vous devez identifier l'abstraction manquante dont vous avez vraiment besoin , puis implémenter cette abstraction à l'aide de threads , puis utilisez l'abstraction .

Eric Lippert
la source
33
Tous de bons conseils. Mais avez-vous un exemple où un ancien objet thread pourrait être préférable à l'une des constructions les plus récentes?
Robert Harvey
Le débogage de plusieurs threads avec des problèmes de synchronisation pour charger du code peut parfois être beaucoup plus facile que d'utiliser d'autres constructions de niveau «supérieur». Et vous avez de toute façon besoin des connaissances de base pour travailler et utiliser Threads.
CodingBarfield
Merci pour la réponse, Eric. Il est vraiment facile d'oublier, sous le bombardement récent des nouvelles fonctionnalités de threading, que parfois l'ancien thread est encore nécessaire. Eh bien, je suppose que la connaissance du bare-metal est utile de toute façon.
Tudor
15
@RobertHarvey: Bien sûr. Par exemple, si vous avez une situation classique de "producteur / consommateur", où vous voulez avoir un thread dédié à la sortie de contenu d'une file d'attente de travail et un autre thread dédié à la mise en file d'attente de travail. Les deux bouclent pour toujours; ils ne correspondent pas bien aux tâches que vous effectuez pendant un certain temps et obtiennent ensuite un résultat. Vous n'avez pas besoin d'un pool de threads car il n'y a que deux threads. Et vous pouvez être intelligent avec votre utilisation des verrous et des signaux pour vous assurer que la file d'attente est maintenue en sécurité sans bloquer l'autre thread pendant une longue période.
Eric Lippert
@Eric: Une telle abstraction n'est-elle pas déjà proposée par TPL Dataflow ?
Allon Guralnek
52

Les threads sont un élément de base pour certaines choses (à savoir le parallélisme et l'asynchronie) et ne doivent donc pas être supprimés. Cependant , pour la plupart des gens et la plupart des cas d'utilisation, il existe des éléments plus appropriés à utiliser que vous avez mentionnés, tels que les pools de threads (qui offrent un bon moyen de gérer de nombreux petits travaux en parallèle sans surcharger la machine en générant 2000 threads à la fois), BackgroundWorker( qui encapsule les événements utiles pour un seul travail de courte durée).

Mais ce n'est pas parce que dans de nombreux cas, ceux-ci sont plus appropriés qu'ils empêchent le programmeur de réinventer inutilement la roue, de faire des erreurs stupides, etc., cela ne signifie pas que la classe Thread est obsolète. Il est toujours utilisé par les abstractions citées ci-dessus et vous en auriez toujours besoin si vous avez besoin d'un contrôle fin sur les threads qui n'est pas couvert par les classes plus spéciales.

Dans le même ordre d'idées, .NETn'interdit pas l'utilisation de tableaux, bien List<T>qu'ils conviennent mieux à de nombreux cas où les gens utilisent des tableaux. Simplement parce que vous voudrez peut-être encore construire des choses qui ne sont pas couvertes par la bibliothèque standard.

Joey
la source
6
Ce n'est pas parce qu'une transmission automatique fonctionne 99% du temps que les transmissions manuelles n'ont aucune valeur, même en ignorant les préférences du conducteur.
Guvante
4
Je ne connais pas très bien les idiomes de conduite étant donné que je suis cycliste et utilisateur des transports publics. Mais une transmission manuelle n'est pas au cœur d'une transmission automatique - ce n'est pas une main mécanique déplaçant un bâton. Ce n'est pas vraiment une abstraction sur l'autre, pour autant que je puisse le voir.
Joey
2
Vous pouvez remplacer le mot "thread" par "code non managé" dans le deuxième paragraphe et il sonnera toujours vrai
Conrad Frix
1
@Joey: Oh, je pensais que vous parliez de la pièce d'obsolescence, de la valeur d'avoir un contrôle fin, au détriment de la convivialité, même si la plupart n'en auront jamais besoin. BTW techniquement, la partie intéressante d'une transmission manuelle est de toute façon l'embrayage.
Guvante
3
Si vous voulez vraiment suivre la métaphore de la transmission, la plupart des transmissions séquentielles (comme la transmission SMG de BMW) sont , en fait, des transmissions manuelles avec un embrayage et un sélecteur de vitesse commandés par ordinateur. Ce ne sont pas des systèmes d'engrenages planétaires comme les automatismes conventionnels.
Adam Robinson
13

Tasket Threadsont des abstractions différentes. Si vous souhaitez modéliser un thread, la Threadclasse reste le choix le plus approprié. Par exemple, si vous avez besoin d'interagir avec le fil actuel, je ne vois pas de meilleurs types pour cela.

Cependant, comme vous le faites remarquer, .NET a ajouté plusieurs abstractions dédiées qui sont préférables Threaddans de nombreux cas.

Brian Rasmussen
la source
9

La Threadclasse n'est pas obsolète, elle est toujours utile dans des circonstances particulières.

Là où je travaille, nous avons écrit un `` processeur d'arrière-plan '' dans le cadre d'un système de gestion de contenu: un service Windows qui surveille les répertoires, les adresses e-mail et les flux RSS, et chaque fois que quelque chose de nouveau apparaît, exécutez une tâche dessus - généralement pour importer le Les données.

Les tentatives d'utilisation du pool de threads pour cela n'ont pas fonctionné: il essaie d'exécuter trop de choses en même temps et de détruire les disques, nous avons donc implémenté notre propre système d'interrogation et d'exécution en utilisant directement la Threadclasse.

MiMo
la source
Je n'ai aucune idée si utiliser Thread directement ici est la bonne solution ici, mais +1 pour donner un exemple où aller à Thread était nécessaire.
Oddthinking
6

Les nouvelles options rendent l' utilisation directe et la gestion des threads (coûteux) moins fréquentes.

les gens qui, lorsqu'ils sont confrontés à une tâche qui implique une sorte d'exécution parallèle, passent directement à l'utilisation de la bonne vieille classe Thread.

Ce qui est une manière très coûteuse et relativement complexe de faire des choses en parallèle.

Notez que la dépense compte le plus: vous ne pouvez pas utiliser un thread complet pour faire un petit travail, ce serait contre-productif. Le ThreadPool combat les coûts, la classe Task les complexités (exceptions, attente et annulation).

Henk Holterman
la source
5

Pour répondre à la question " Y a-t-il des cas où il est nécessaire ou utile d'utiliser un vieil objet Thread ?" t jamais interagir avec à partir d'un fil différent.

Par exemple, si vous écrivez une application qui s'abonne pour recevoir des messages d'une sorte de file d'attente de messages et que votre application va faire plus que simplement traiter ces messages, il serait utile d'utiliser un thread car le thread sera autonome (c'est-à-dire que vous n'attendez pas que cela soit fait), et ce n'est pas de courte durée. L'utilisation de la classe ThreadPool sert davantage à mettre en file d'attente un tas d'éléments de travail de courte durée et à permettre à la classe ThreadPool de gérer efficacement le traitement de chacun d'entre eux lorsqu'un nouveau Thread est disponible. Les tâches peuvent être utilisées là où vous utiliseriez directement Thread, mais dans le scénario ci-dessus, je ne pense pas qu'elles vous achèteraient beaucoup. Ils vous aident à interagir plus facilement avec le fil (ce que le scénario ci-dessus ne fait pas

Derek Greer
la source
Je suis d'accord que la plupart des nouvelles choses intéressantes de parallélisme sont conçues autour de petites tâches à grain fin à court terme, et que les threads de longue durée devraient toujours être implémentés avec System.IO.Threading.Thread.
Ben Voigt
4

Probablement pas la réponse que vous attendiez, mais j'utilise Thread tout le temps lors du codage avec .NET Micro Framework. MF est assez réduit et n'inclut pas d'abstractions de plus haut niveau et la classe Thread est super flexible lorsque vous avez besoin d'obtenir le dernier bit de performance d'un processeur à faible MHz.

AngryHacker
la source
Hé, c'est en fait un très bon exemple! Merci. Je n'ai pas pensé aux frameworks qui ne prennent pas en charge les nouvelles fonctionnalités.
Tudor
3

Vous pouvez comparer la classe Thread à ADO.NET. Ce n'est pas l'outil recommandé pour faire le travail, mais il n'est pas obsolète. D'autres outils s'y ajoutent pour faciliter le travail.

Ce n'est pas faux d'utiliser la classe Thread sur d'autres choses, surtout si ces choses ne fournissent pas une fonctionnalité dont vous avez besoin.

Kyle Trauberman
la source
3

Ce n'est pas définitivement obsolète.

Le problème avec les applications multithreads est qu'elles sont très difficiles à obtenir (le comportement, l'entrée, la sortie et l'état interne souvent indéterministes sont importants), donc un programmeur doit pousser autant de travail que possible vers le framework / les outils. Abstenez-le. Mais l'ennemi mortel de l'abstraction est la performance.

Ma question est donc la suivante: y a-t-il des cas où il est nécessaire ou utile d'utiliser un ancien objet Thread au lieu de l'une des constructions ci-dessus?

J'irais avec les threads et les verrous uniquement s'il y a de sérieux problèmes de performances, des objectifs de haute performance.

Lukasz Madon
la source
3

J'ai toujours utilisé la classe Thread lorsque j'ai besoin de compter et de contrôler les threads que j'ai lancés. Je me rends compte que je pourrais utiliser le pool de threads pour contenir tout mon travail exceptionnel, mais je n'ai jamais trouvé de bon moyen de garder une trace de la quantité de travail actuellement en cours ou de son statut.

Au lieu de cela, je crée une collection et y place les fils après les avoir tournés - la toute dernière chose qu'un fil fait est de se retirer de la collection. De cette façon, je peux toujours dire combien de threads sont en cours d'exécution, et je peux utiliser la collection pour demander à chacun ce qu'il fait. S'il y a un cas où je dois tous les tuer, normalement vous devez définir une sorte d'indicateur "Abandonner" dans votre application, attendez que chaque thread remarque qu'il se termine seul - dans mon cas, je peut parcourir la collection et émettre un Thread.Abort à chacun à son tour.

Dans ce cas, je n'ai pas trouvé de meilleur moyen de travailler directement avec la classe Thread. Comme Eric Lippert l'a mentionné, les autres ne sont que des abstractions de niveau supérieur, et il est approprié de travailler avec les classes de niveau inférieur lorsque les implémentations de haut niveau disponibles ne répondent pas à vos besoins. Tout comme vous devez parfois faire des appels d'API Win32 lorsque .NET ne répond pas exactement à vos besoins, il y aura toujours des cas où la classe Thread est le meilleur choix malgré les "avancées" récentes.

SqlRyan
la source