Quelle est la différence entre la tâche et le thread?

378

En C # 4.0, nous avons Taskdans l' espace de noms System.Threading.Tasks . Quelle est la vraie différence entre Threadet Task. J'ai fait un exemple de programme (aide tirée de MSDN) pour mon propre plaisir d'apprendre avec

Parallel.Invoke 
Parallel.For 
Parallel.ForEach 

mais ont de nombreux doutes car l'idée n'est pas si claire.

J'ai initialement recherché dans Stackoverflow un type de question similaire mais peut-être avec ce titre de question, je n'ai pas pu obtenir le même. Si quelqu'un connaît le même type de question posté ici plus tôt, veuillez donner la référence du lien.

hippietrail
la source
8
threads exécuter des tâches
pm100

Réponses:

315

Une tâche est quelque chose que vous voulez faire.

Un thread est l'un des nombreux travailleurs possibles qui effectuent cette tâche.

En termes .NET 4.0, une tâche représente une opération asynchrone. Les threads sont utilisés pour terminer cette opération en divisant le travail en morceaux et en les affectant à des threads séparés.

Blé Mitch
la source
Pourriez-vous fournir un exemple rudimentaire de threads travaillant pour effectuer une tâche? Je ne sais pas si les fils effectuent un travail indépendant les uns des autres ou font-ils un calcul de travail d'équipe ?
pensum
Les deux scénarios sont possibles: dans une situation optimale, les threads effectuent un travail indépendant sans avoir besoin de se synchroniser avec d'autres threads. En pratique, les verrous sont utilisés pour coordonner les threads.
Mitch Wheat
451

En termes informatiques, a Taskest un avenir ou une promesse . (Certaines personnes utilisent ces deux termes de manière synomymique, d'autres les utilisent différemment, personne ne peut s'entendre sur une définition précise .) Fondamentalement, un Task<T>"promet" de vous rendre un T, mais pas pour l'instant chérie, je suis un peu occupé, pourquoi ne pas tu reviens plus tard?

A Threadest un moyen de réaliser cette promesse. Mais tout le monde Taskn'a pas besoin d'un tout nouveau Thread. (En fait, la création d'un thread est souvent indésirable, car cela coûte beaucoup plus cher que de réutiliser un thread existant à partir du pool de threads. Plus d'informations à ce sujet dans un instant.) Si la valeur que vous attendez provient du système de fichiers ou d'un base de données ou le réseau, alors il n'est pas nécessaire pour un thread de s'asseoir et d'attendre les données quand il peut répondre à d'autres demandes. Au lieu de cela, le Taskpeut enregistrer un rappel pour recevoir la ou les valeurs lorsqu'ils sont prêts.

En particulier, le Taskne dit pas pourquoi il faut autant de temps pour renvoyer la valeur. Il se peut que cela prenne beaucoup de temps à calculer, ou que cela prenne beaucoup de temps à récupérer. Ce n'est que dans le premier cas que vous utiliseriez a Threadpour exécuter a Task. (Dans .NET, les threads coûtent très cher, vous devez donc généralement les éviter autant que possible et ne les utiliser que si vous souhaitez exécuter plusieurs calculs lourds sur plusieurs processeurs. Par exemple, sous Windows, un thread pèse 12 Ko ( Je pense), sous Linux, un thread ne pèse que 4 Ko, dans Erlang / BEAM même seulement 400 octets. En .NET, c'est 1 MiByte!)

Jörg W Mittag
la source
29
Fait intéressant, dans les premières versions de prévisualisation de TPL (Task Parallel Library), il y avait Task and Future <T>. Le futur <T> a ensuite été renommé en tâche <T>. :)
Lee Campbell
23
Comment avez-vous calculé 1 Mo pour .NET?
dvallejo
5
@DanVallejo: Ce nombre a été mentionné dans une interview avec l'équipe de conception de TPL. Je ne peux pas vous dire qui l'a dit ou quelle interview c'était, j'ai regardé ça il y a des années.
Jörg W Mittag
9
@RIPUNJAYTRIPATHI Bien sûr, mais il n'est pas nécessaire que ce soit un autre thread, ce pourrait être le thread qui a demandé le travail en premier lieu.
Chris Pitman
7
.NET utilise uniquement des threads Windows sur Windows, donc la taille est la même - par défaut, généralement 1 Mo de mémoire virtuelle pour les deux. La mémoire physique est utilisée selon les besoins dans les blocs de taille de page (généralement 64 Ko), de même avec le code natif. La taille minimale de la pile de threads dépend du système d'exploitation - 256 Ko pour Vista, par exemple. Sous Linux x86, la valeur par défaut est généralement de 2 Mio - encore une fois, allouée en morceaux de la taille d'une page. (simplification) Erlang n'utilise qu'un seul thread système par processus, ces 400 octets se réfèrent à quelque chose de similaire aux .NET Task.
Luaan
39

Fil

Le truc du métal nu, vous n'avez probablement pas besoin de l'utiliser, vous pouvez probablement utiliser une LongRunningtâche et profiter des avantages de la bibliothèque parallèle de tâches TPL, incluse dans .NET Framework 4 (février 2002) et au-dessus (également .NET Coeur).

Tâches

Abstraction au-dessus des fils. Il utilise le pool de threads (sauf si vous spécifiez la tâche comme une LongRunningopération, si c'est le cas, un nouveau thread est créé sous le capot pour vous).

Pool de threads

Comme son nom l'indique: un pool de threads. Le framework .NET gère-t-il un nombre limité de threads pour vous? Pourquoi? Parce que l'ouverture de 100 threads pour exécuter des opérations CPU coûteuses sur un processeur avec seulement 8 cœurs n'est certainement pas une bonne idée. Le framework conservera ce pool pour vous, réutilisant les threads (ne les créant / tuant pas à chaque opération) et exécutant certains d'entre eux en parallèle, de manière à ce que votre CPU ne brûle pas.

OK, mais quand utiliser chacun d'eux?

En résumé: utilisez toujours les tâches.

La tâche est une abstraction, elle est donc beaucoup plus facile à utiliser. Je vous conseille de toujours essayer d'utiliser des tâches et si vous rencontrez un problème qui vous oblige à gérer un thread par vous-même (probablement 1% du temps), utilisez des threads.

MAIS sachez que:

  • Limites d'E / S : pour les opérations liées aux E / S (appels de base de données, fichiers de lecture / écriture, appels d'API, etc.), évitez d'utiliser des tâches normales, utilisez des LongRunningtâches ( ou des threads si vous en avez besoin ). Parce que l'utilisation de tâches vous mènerait à un pool de threads avec quelques threads occupés et beaucoup d'autres tâches attendant son tour pour prendre le pool.
  • CPU Bound : Pour les opérations liées au CPU, utilisez simplement les tâches normales (qui utiliseront en interne le pool de threads) et soyez heureux.
fabriciorissetto
la source
légère correction, un Thread n'est pas une "chose nue". il est implémenté par le système d'exploitation, la plupart des implémentations reposent sur des fonctionnalités du CPU et du CS, mais elles ne sont pas implémentées par le matériel.
Tomer W
7

Vous pouvez utiliser Taskpour spécifier ce que vous voulez faire puis l'attacher Taskavec un Thread. de sorte que ce Taskserait exécuté dans ce nouveau fait Threadplutôt que sur le thread GUI.

À utiliser Taskavec TaskFactory.StartNew(Action action). Ici, vous exécutez un délégué, donc si vous n'avez utilisé aucun thread, il sera exécuté dans le même thread (thread GUI). Si vous mentionnez un thread, vous pouvez l'exécuter Taskdans un autre thread. Il s'agit d'un travail inutile car vous pouvez exécuter directement le délégué ou attacher ce délégué à un thread et exécuter ce délégué dans ce thread. Alors ne l'utilisez pas. c'est juste inutile. Si vous avez l'intention d'optimiser votre logiciel, c'est un bon candidat à supprimer.

** Veuillez noter que Actionc'est un delegate.

Gryphes
la source
6

En plus des points ci-dessus, il serait bon de savoir que:

  1. Une tâche est par défaut une tâche d'arrière-plan. Vous ne pouvez pas avoir de tâche de premier plan. D'un autre côté, un thread peut être en arrière-plan ou au premier plan (utilisez la propriété IsBackground pour modifier le comportement).
  2. Les tâches créées dans le pool de threads recyclent les threads, ce qui permet d'économiser des ressources. Dans la plupart des cas, les tâches doivent donc être votre choix par défaut.
  3. Si les opérations sont rapides, il est préférable d'utiliser une tâche plutôt qu'un fil. Pour les opérations de longue durée, les tâches n'offrent pas beaucoup d'avantages par rapport aux threads.
user2492339
la source
4

J'utilise habituellement Taskpour interagir avec Winforms et un simple travailleur en arrière-plan pour éviter de geler l'interface utilisateur. ici un exemple quand je préfère utiliserTask

private async void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    await Task.Run(() => {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
    })
    buttonDownload.Enabled = true;
}

CONTRE

private void buttonDownload_Click(object sender, EventArgs e)
{
    buttonDownload.Enabled = false;
    Thread t = new Thread(() =>
    {
        using (var client = new WebClient())
        {
            client.DownloadFile("http://example.com/file.mpeg", "file.mpeg");
        }
        this.Invoke((MethodInvoker)delegate()
        {
            buttonDownload.Enabled = true;
        });
    });
    t.IsBackground = true;
    t.Start();
}

la différence est que vous n'avez pas besoin d'utiliser MethodInvokerun code plus court.

ewwink
la source
4

La tâche est comme une opération que vous souhaitez effectuer, Thread aide à gérer ces opérations via plusieurs nœuds de processus. tâche est une option légère comme Threading peut conduire à une gestion de code complexe ,
je vous suggère de lire à partir de MSDN (meilleur dans le monde) toujours la

tâche

Fil

Saurabh
la source
3

Une tâche peut être considérée comme un moyen pratique et facile d'exécuter quelque chose de manière asynchrone et en parallèle.

Normalement, une tâche est tout ce dont vous avez besoin, je ne me souviens pas si j'ai déjà utilisé un fil pour autre chose que l'expérimentation.

Vous pouvez accomplir la même chose avec un fil (avec beaucoup d'efforts) que vous pouvez avec une tâche.

Fil

int result = 0;
Thread thread = new System.Threading.Thread(() => { 
    result = 1; 
});
thread.Start();
thread.Join();
Console.WriteLine(result); //is 1

Tâche

int result = await Task.Run(() => {
    return 1; 
});
Console.WriteLine(result); //is 1

Une tâche utilise par défaut le Threadpool, ce qui économise des ressources car la création de threads peut être coûteuse. Vous pouvez voir une tâche comme une abstraction de niveau supérieur sur les threads.

Comme le souligne cet article , la tâche fournit les fonctionnalités puissantes suivantes sur le fil.

  • Les tâches sont réglées pour tirer parti des processeurs multicœurs.

  • Si le système a plusieurs tâches, il utilise le pool de threads CLR en interne et n'a donc pas la surcharge associée à la création d'un thread dédié à l'aide du thread. Réduisez également le temps de changement de contexte entre plusieurs threads.

  • La tâche peut renvoyer un résultat. Il n'y a pas de mécanisme direct pour renvoyer le résultat du thread.
  • Attendez un ensemble de tâches, sans construction de signalisation.

  • Nous pouvons enchaîner les tâches pour les exécuter les unes après les autres.

  • Établissez une relation parent / enfant lorsqu'une tâche est démarrée à partir d'une autre tâche.

  • L'exception de tâche enfant peut se propager à la tâche parent.

  • Annulation de la prise en charge des tâches grâce à l'utilisation de jetons d'annulation.

  • L'implémentation asynchrone est facile dans la tâche, en utilisant les mots clés «async» et «attendre».

CharithJ
la source