web-dev-qa-db-ja.com

タスク(TPL)ライブラリを使用すると、アプリケーションがマルチスレッド化されますか?

最近インタビューを受けた時、私はこの質問を受けました。

Q:マルチスレッドアプリケーションを作成しましたか?

A:はい

Q:詳細を説明しますか?

A:Tasks(タスク並列ライブラリ)を使用して、waiting for some info from internet while loading UIのようないくつかのタスクを実行しました。これにより、アプリケーションの使いやすさが向上します。

Q:しかし、TPLを使用しただけで、multithreadedアプリケーションを作成したことになりますか?

私:(何を言ったらいいかわからない1)

では、マルチスレッドアプリケーションとは一体何でしょうか。 Tasksを使用することとは異なりますか?

タスクcanは、複数のスレッドで実行される操作を表すために使用されますが、haveには対応していません。 1つのcan単一のスレッドでのみ実行される複雑なTPLアプリケーションを記述します。たとえば、一部のデータに対するネットワーク要求を表すタスクがある場合、そのタスクはnotであり、その目的を達成するために追加のスレッドを作成します。そのようなプログラムは(うまくいけば)非同期ですが、必ずしもマルチスレッドではありません。

並列処理は、同時に複数のことを実行しています。これは、複数のスレッドの結果である場合とそうでない場合があります。

ここで類推していきましょう。


ボブが夕食を作る方法は次のとおりです。

  1. 彼は鍋に水を入れ、沸騰させます。
  2. 次にパスタを水に入れます。
  3. 終わったら、パスタを空にします。
  4. 彼はソースの材料を準備します。
  5. 彼はソースの材料をすべて鍋に入れます。
  6. 彼はソースを作ります。
  7. 彼はパスタにソースをかける。
  8. 彼は夕食を食べます。

ボブはディナーを調理するときに、マルチスレッド、非同期、または並列処理なしで完全に同期して調理しました。


ジェーンが夕食を作る方法は次のとおりです。

  1. 彼女は鍋に水を入れ、沸騰させます。
  2. 彼女はソースの材料を準備します。
  3. 彼女はパスタを沸騰したお湯に入れます。
  4. 彼女は鍋に材料を入れます。
  5. 彼女はパスタを排水します。
  6. 彼女はパスタにソースをかける。
  7. 彼女は夕食を食べます。

ジェーンは、(マルチスレッドを使用せずに)非同期クッキングを利用して、ディナーを調理するときに並列処理を実現しました。


サービーがディナーを調理する方法は次のとおりです。

  1. 彼はボブに水を沸騰させ、準備ができたらパスタを入れ、パスタを出すように指示します。
  2. 彼はジェーンにソースの材料を準備し、それを調理し、そしてパスタの上にそれが終わったらそれを出すように命じます。
  3. 彼はボブとジェーンが終わるのを待つ。
  4. 彼は夕食を食べます。

Servyは複数のスレッド(ワーカー)を活用し、それぞれが個別に同期して作業を行いましたが、並列処理を実現するために互いに非同期で作業しました。

もちろん、たとえば、ストーブにバーナーが2つあるのか1つしかないのかを考えると、これはなおさら興味深いものになります。私たちのストーブに2つのバーナーがある場合、ボブとジェーンの2つのスレッドはどちらも、お互いに干渉することなく作業を行うことができます。彼らは少し肩をぶつけたり、時々同じキャビネットから何かをつかんだりするので、それぞれbitが遅くなりますが、それほどではありません。ただし、それぞれが1つのストーブバーナーを共有する必要がある場合、他の人が仕事をしているときはいつでも、実際にはまったく多くのことを行うことができません。その場合、ボブが一人でいるときのように、料理を1人で完全に同期して調理するよりも、実際に作業が速く完了することはありません。この場合、複数のスレッドでクッキングしていますただし、クッキングは並列化されていませんすべてのマルチスレッド作業が実際に並列作業であるわけではありません。これは、1つのCPUを搭載したマシンで複数のスレッドを実行しているときに起こります。各スレッドは順番に作業を行っているので、実際には、1つのスレッドを使用するよりも早く作業を完了することはありません。 (これは、マルチスレッドプログラムが1つのコアCPUで無意味であることを意味するのではなく、そうではありません。それらを使用する理由は、速度を向上させるためではないということです。)


これらのクックがタスクパラレルライブラリを使用して作業を行う方法を検討して、TPLの使用がこれらのタイプのクックのそれぞれに対応することを確認することもできます。

したがって、最初にbobを作成し、通常の非TPLコードを記述してすべてを同期的に実行します。

public class Bob : ICook
{
    public IMeal Cook()
    {
        Pasta pasta = PastaCookingOperations.MakePasta();
        Sauce sauce = PastaCookingOperations.MakeSauce();
        return PastaCookingOperations.Combine(pasta, sauce);
    }
}

次に、2つの非同期操作を開始し、それぞれの結果を計算するためにそれぞれを開始した後、両方の操作を待機するJaneがあります。

public class Jane : ICook
{
    public IMeal Cook()
    {
        Task<Pasta> pastaTask = PastaCookingOperations.MakePastaAsync();
        Task<Sauce> sauceTask = PastaCookingOperations.MakeSauceAsync();
        return PastaCookingOperations.Combine(pastaTask.Result, sauceTask.Result);
    }
}

ここで覚えておくと、ジェーンはTPLを使用しており、多くの作業を並行して行っていますが、彼女は単一スレッドを使用して作業を行っています。

次に、Task.Runを使用して別のスレッドで作業を行うことを表すのタスクを作成するServyがあります。彼は2つの異なるワーカーを開始し、それぞれが同期的にいくつかの作業を行ってから、両方のワーカーが完了するまで待機します。

public class Servy : ICook
{
    public IMeal Cook()
    {
        var bobsWork = Task.Run(() => PastaCookingOperations.MakePasta());
        var janesWork = Task.Run(() => PastaCookingOperations.MakeSauce());
        return PastaCookingOperations.Combine(bobsWork.Result, janesWork.Result);
    }
}
99
Servy

Taskは、将来の作業を完了するための約束です。使用する場合は、I/O basedの作業に使用できます。これはしないコードの実行に複数のスレッドを使用する必要があります。良い例は、C#5のasync/awaitの機能と、HttpClientを使用してネットワークベースのI/O作業を行うことです。

ただし、TPLを利用してマルチスレッド処理を行うことができます。たとえば、Task.RunまたはTask.Factory.Startnewを使用して新しいタスクを開始すると、バックグラウンドでThreadPoolの作業がキューに入れられ、TPLが抽象化して複数のスレッドを使用できるようになります。

複数のスレッドを使用する一般的なシナリオは、同時に(並行して)実行できるCPUにバインドされた作業がある場合です。マルチスレッドアプリケーションの操作には大きな責任が伴います。

したがって、TPLを使用することは、必ずしも複数のスレッドを使用することを意味しないことがわかりますが、マルチスレッドを実行するためにそれを確実に活用できます。

4
Yuval Itzchakov

Q:しかし、TPLを使用しただけでは、マルチスレッドアプリケーションを作成したことになりますか?

スマートな質問、タスク!=マルチスレッド、 TaskCompletionSource を使用して、Taskを作成し、シングルスレッド(UIスレッドのみの場合もある)自体で実行できます。

Taskは、将来完了する可能性のある操作を抽象化したものです。コードがマルチスレッドであることを意味するものではありません。通常Taskにはマルチスレッドが含まれますが、常にそうであるとは限りません。

また、TPLの知識があることだけを忘れないでください。マルチスレッドを知っているとは言えません。カバーする必要がある多くの概念があります。

  • Thread
  • 同期プリミティブ
  • スレッドセーフ
  • 非同期プログラミング
  • TPLの一部である並列プログラミング。
  • そしてもっと...

もちろんタスク並列ライブラリも。

注:これは完全なリストではありません。これらは私の頭の上からのものです。


スレッドのみの場合は、 http://www.albahari.com/threading/ をお勧めします

ビデオチュートリアルについては、 Pluralsight をお勧めします。それは支払われますが、コストに見合う価値があります。

最後になりましたが、もちろん、はい Stackoverflow です。

3

私の5セント:TPLアプリケーションをマルチスレッド化するために、_Task.Run_または_Task.Factory.StartNew_で明示的にスレッドを使用する必要はありません。このことを考慮:

_async static Task TestAsync()
{
    Func<Task> doAsync = async () =>
    {
        await Task.Delay(1).ConfigureAwait(false);
        Console.WriteLine(new { Thread.CurrentThread.ManagedThreadId });
    };

    var tasks = Enumerable.Range(0, 10).Select(i => doAsync());
    await Task.WhenAll(tasks);
}

// ...
TestAsync().Wait();
_

await内のdoAsyncの後のコードは、異なるスレッドで同時に実行されます。同様の方法で、非同期ソケットAPI、HttpClient、_Stream.ReadAsync_、またはスレッドプール(IOCPプールスレッドを含む)を使用するその他のもので並行性を導入できます。

比喩的に言えば、フレームワークはThreadPoolを広範囲に使用するため、すべての.NETアプリケーションはすでにマルチスレッド化されています。単純なコンソールアプリケーションでも、System.Diagnostics.Process.GetCurrentProcess().Threads.Countに対して複数のスレッドが表示されます。あなたのインタビュアーは、あなたがconcurrent(またはparallel)コードを書いたかどうか尋ねてきたはずです。

1
noseratio