web-dev-qa-db-ja.com

タスクとスレッドの違いは何ですか?

C#4.0では、System.Threading.Tasks名前空間にTaskがあります。 ThreadTaskの本当の違いは何ですか。自分で学習するために、サンプルプログラムをいくつか作成しました(MSDNから取得したヘルプ)。

Parallel.Invoke 
Parallel.For 
Parallel.ForEach 

しかし、アイデアがあまり明確ではないため、多くの疑問があります。

私は最初にStackoverflowで同様の種類の質問を検索しましたが、この質問のタイトルでも同じにはなれませんでした。誰かが以前にここに投稿されているのと同じタイプの質問について知っているならば、親切にリンクの参照をしてください。

349
user372724

タスクはあなたがやりたいことです。

スレッドはそのタスクを実行する多くの可能なワーカーの1つです。

.NET 4.0の用語では、 タスク は非同期操作を表します。スレッドを使用して、作業をチャンクに分割し、別々のスレッドに割り当てることによってその操作を完了します。

292
Mitch Wheat

コンピュータサイエンスの用語では、Task未来またはの約束です。 (これらの2つの用語を同義的に使用する人も、異なる使用法を使用する人もいます。正確な定義に同意できない人もいます。)基本的にTask<T>は「T」を返します。今蜂蜜、私はちょっと忙しいです、なぜあなたは後で戻って来ませんか?

Threadはその約束を果たす方法です。しかし、すべてのTaskが真新しいThreadを必要とするわけではありません。 (実際には、スレッドプールから既存のスレッドを再利用するよりもはるかにコストがかかるので、スレッドを作成するのは望ましくありません。待っている値がファイルシステムまたはデータベースやネットワークの場合は、スレッドが他の要求を処理できるときには待機してデータを待つ必要はありません。代わりに、Taskは、準備ができたら値を受け取るためのコールバックを登録するかもしれません。

特にTaskとは言いませんと言うわけではありませんそれは値を返すのに非常に長い時間がかかるということです。 計算に長い時間がかかるかもしれませんし、フェッチするには長い時間がかかるかもしれません。前者の場合のみ、Threadを実行するためにTaskを使用します。 (.NETでは、スレッドは非常に高価になっているので、一般的にはできるだけスレッドを避け、複数のCPUで複数の重い計算を実行したい場合にのみ使用します。たとえば、Windowsでは、スレッドの重量は12KiByte(私の考えでは、Linuxではスレッドの長さは4 Kバイト、Erlang/BEAMではわずか400バイト、.NETでは1 Mバイトです。)

425
Jörg W Mittag

Thread

ベアメタルなので、おそらく使う必要はないでしょう。おそらくLongRunningタスクを使って、.NET Framework 4(2002年2月)以降に含まれているTPL - タスク並列ライブラリの恩恵を受けることができます。 NETコア)。

タスク

スレッドの上の抽象化。これはスレッドプールを使用します(タスクをLongRunning操作として指定しない限り、指定した場合は、新しいスレッドがフードの下に作成されます)。

スレッドプール

名前が示すように、スレッドのプール。 .NETフレームワークは限られた数のスレッドを処理していますか。どうして?わずか8コアのプロセッサ上で高価なCPU操作を実行するために100個のスレッドを開くのは、絶対にお勧めできません。フレームワークはあなたのためにこのプールを維持し、スレッドを再利用し(各操作でそれらを作成したり殺したりしないで)、そしてあなたのCPUが燃えないようにそれらのいくつかを並列に実行します。

それでは、いつそれぞれを使うのですか。

再開にあたって:常にタスクを使用する。

タスクは抽象化されているので、はるかに使いやすいです。私はあなたがいつもタスクを使うことを試みることを勧めます、そしてあなたがあなた自身でスレッドを扱う必要があるような問題に直面するなら(おそらく1%の時間)それからスレッドを使うこと。

ただし、以下の点に注意してください。

  • I/Oバウンド:I/Oバウンド操作(データベース呼び出し、ファイルの読み取り/書き込み、API呼び出しなど)では、通常のタスクの使用を避け、LongRunningを使用してください。タスク(またはが必要な場合はスレッド)。タスクを使用すると、少数のスレッドがビジー状態になり、プールを占有する順番を待っている別のタスクが多数存在するスレッドプールに移動します。
  • CPUバウンド:CPUバウンド操作には通常のタスク(内部的にはスレッドプールを使用する)を使用してください。
29

Taskを使用して何をしたいのかを指定してから、そのTaskThreadに添付することができます。そのため、Taskは、GUIスレッドではなく、新しく作成されたThreadで実行されます。

TaskTaskFactory.StartNew(Action action)と共に使用します。ここではデリゲートを実行するので、スレッドを使用していなければ、同じスレッド(GUIスレッド)で実行されます。スレッドに言及した場合は、このTaskを別のスレッドで実行できます。これは不要な作業です。デリゲートを直接実行するか、そのデリゲートをスレッドにアタッチしてそのスレッドで実行することができます。だからそれを使わないでください。それはただ不要です。あなたのソフトウェアを最適化するつもりなら、これは削除されるべき良い候補です。

** Actiondelegateです。

7
Gryphes

上記の点に加えて、以下のことを知っておくとよいでしょう。

  1. タスクはデフォルトではバックグラウンドタスクです。前景の仕事はできません。一方、スレッドはバックグラウンドでもフォアグラウンドでもかまいません(動作を変更するにはIsBackgroundプロパティを使用します)。
  2. スレッドプールで作成されたタスクはスレッドをリサイクルし、リソースの節約に役立ちます。そのため、ほとんどの場合、タスクはデフォルトの選択肢です。
  3. 操作が速い場合は、スレッドではなくタスクを使用するほうがはるかに良いです。実行時間が長い操作の場合、タスクはスレッドよりも大きな利点はありません。
6
user2492339

タスクは、実行したい操作のようなものです。スレッドは、複数のプロセスノードを通してそれらの操作を管理するのに役立ちます。スレッド化は複雑なコード管理につながる可能性があるため、タスクは軽量のオプションです。
常にMSDN(Best in world)から読むことをお勧めします

タスク

スレッド

2
Saurabh

私は通常、Taskを使ってWinformsや単純なバックグラウンドワーカーとやり取りし、UIがフリーズしないようにします。これは私がTaskを使うことを好むときの例です

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;
}

VS

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();
}

違いはMethodInvokerとそれより短いコードを使う必要がないということです。

2
ewwink