Création de threads - Task.Factory.StartNew vs new Thread ()

102

J'apprends juste les nouvelles bibliothèques Threading et Parallel dans .Net 4

Dans le passé, je créerais un nouveau Thread comme ceci (à titre d'exemple):

DataInThread = new Thread(new ThreadStart(ThreadProcedure));
DataInThread.IsBackground = true;
DataInThread.Start();

Maintenant je peux faire:

Task t = Task.Factory.StartNew(() =>
{
   ThreadProcedure();
});

Quelle est la différence le cas échéant?

Merci

Jon
la source
1
Vous devrez vous inquiéter un peu du fonctionnement du planificateur de pool de threads. Cela peut faire une grande différence, mais tout dépend de ce que vous faites réellement à l'intérieur du fil.
Hans Passant

Réponses:

79

Il y a une grosse différence. Les tâches sont planifiées sur le ThreadPool et peuvent même être exécutées de manière synchrone si cela est approprié.

Si vous avez un travail d'arrière-plan de longue durée, vous devez le spécifier en utilisant l'option de tâche appropriée.

Vous devriez préférer la bibliothèque parallèle de tâches à la gestion explicite des threads, car elle est plus optimisée. Vous avez également plus de fonctionnalités telles que la continuation.

sanosdole
la source
5
Non, non. Cela ne fait que commencer les tâches. Cela pourrait mettre la tâche en file d'attente dans le pool de threads ou l'exécuter de manière synchrone. Le TPL vise à vous libérer de la gestion des threads / de la concurrence et d'utiliser le meilleur pour votre plate-forme (comme l'utilisation de cœurs)
sanosdole
10
Il existe l'option TaskCreationOptions.LongRunning qui créera toujours un autre thread, mais l'essentiel est de savoir pourquoi avez-vous besoin d'un autre thread? Si vous voulez juste faire quelque chose en parallèle (Main fait qc pendant que la tâche s'exécute), il est préférable de laisser une bibliothèque optimisée décider comment utiliser les ressources système comme les threads pour le faire de la manière la plus efficace.
sanosdole
3
Cet article msdn décrit comment les tâches sont planifiées. Il couvre la longue durée et l'inlining (exécution synchrone). msdn.microsoft.com/en-us/library/dd997402.aspx
sanosdole
2
@sming Le fait est que vous voulez traiter simultanément (sans bloquer l'interface utilisateur) et non que vous voulez un nouveau thread. Le ThreadPool ne bloquera pas le thread d'interface utilisateur, mais gérera les threads d'arrière-plan beaucoup plus efficacement que vous ne pourriez le faire manuellement en créant des threads. C'est le changement du processus mental que le TPL introduit. Ne pensez pas aux threads, pensez aux tâches simultanées.
sanosdole
4
@sming Désolé, cette phrase était un peu trop grossière. L'exécution synchrone des tâches est appelée inlining. Lors de la planification d'une tâche sur le pool de threads (planificateur par défaut) à partir du thread d'interface utilisateur, cela ne se produira pas. Cela ne se produira que si le planificateur ambiant ('TaskScheduler.Current') est le même que le planificateur d'une tâche que vous appelez '.Wait ()' sur. Comme '.Wait ()' bloque, il bloquera de toute façon l'interface utilisateur. Bref: n'attendez pas et il ne sera pas exécuté de manière synchrone.
sanosdole
74

La tâche vous donne toute la bonté de l'API de tâche:

  • Ajout de continuations ( Task.ContinueWith)
  • En attente de la fin de plusieurs tâches (toutes ou toutes)
  • Capturer les erreurs dans la tâche et les interroger plus tard
  • Capturer l'annulation (et vous permettre de spécifier l'annulation pour commencer)
  • Ayant potentiellement une valeur de retour
  • Utilisation de await en C # 5
  • Meilleur contrôle de la planification (si cela va être de longue durée, dites-le lorsque vous créez la tâche pour que le planificateur de tâches puisse en tenir compte)

Notez que dans les deux cas, vous pouvez simplifier légèrement votre code avec les conversions de groupes de méthodes:

DataInThread = new Thread(ThreadProcedure);
// Or...
Task t = Task.Factory.StartNew(ThreadProcedure);
Jon Skeet
la source
8
+1. Je voudrais ajouter que Threadc'est de très bas niveau par rapport à Task(j'ai un article de blog qui entre dans les détails). Je donne une sorte de conférence «Utiliser les tâches dans le monde réel» à Grand Rapids DevDay . Le discours s'appelle "Thread is Dead", car il n'y a plus besoin de Thread(sauf si vous implémentez a TaskScheduler).
Stephen Cleary
@StephenCleary, je suppose que vous voulez dire Threadest mort, quand il s'agit d'être utilisé comme fil d'arrière-plan?
reflux du
1
@ebb: Non, je prends la position la plus forte décrite dans mon premier commentaire. Il n'y a rien à Threadfaire (ou BackgroundWorker) qui ne puisse être fait avec plus d'élégance Tasket une solution appropriée TaskScheduler.
Stephen Cleary
1
@StephenCleary, Comment créerais-tu un fil dédié, sans utiliser Thread?
reflux du
4
@ebb: "Fil dédié" n'est pas clair pour moi. Si vous souhaitez Taskexécuter un sur un thread spécifique, utilisez un fichier approprié TaskScheduler, par exemple AsyncContextThread. Cependant, ce n'est généralement pas nécessaire; les programmateurs SynchronizationContext, ThreadPoolet ConcurrentExclusiveSchedulerPairsont suffisants pour la plupart des programmes.
Stephen Cleary
12

Dans le premier cas, vous démarrez simplement un nouveau thread tandis que dans le second cas, vous entrez dans le pool de threads.

Le travail du pool de threads consiste à partager et à recycler les threads. Cela permet d'éviter de perdre quelques millisecondes à chaque fois que nous devons créer un nouveau thread.

Il existe plusieurs façons d'accéder au pool de threads:

  • avec le TPL (Task Parallel Library) comme vous l'avez fait
  • en appelant ThreadPool.QueueUserWorkItem
  • en appelant BeginInvoke sur un délégué
  • lorsque vous utilisez un BackgroundWorker
alexandrekow
la source
1

Votre premier bloc de code indique à CLR de créer pour vous un thread (par exemple. T) qui peut être exécuté en arrière-plan (utilisez les threads de pool de threads lors de la planification de T). En résumé, vous demandez explicitement à CLR de créer un thread pour que vous puissiez faire quelque chose et appelez la méthode Start () sur le thread pour démarrer.

Votre deuxième bloc de code fait la même chose, mais délègue (transfert implicite) la responsabilité de créer un thread (en arrière-plan, qui s'exécute à nouveau dans le pool de threads) et le thread de départ via la méthode StartNew dans l'implémentation de Task Factory.

C'est une différence rapide entre les blocs de code donnés. Cela dit, il y a peu de différences détaillées que vous pouvez google ou voir d'autres réponses de mes collègues contributeurs.

s_nair
la source