最近インタビューを受けた時、私はこの質問を受けました。
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であり、その目的を達成するために追加のスレッドを作成します。そのようなプログラムは(うまくいけば)非同期ですが、必ずしもマルチスレッドではありません。
並列処理は、同時に複数のことを実行しています。これは、複数のスレッドの結果である場合とそうでない場合があります。
ここで類推していきましょう。
ボブが夕食を作る方法は次のとおりです。
ボブはディナーを調理するときに、マルチスレッド、非同期、または並列処理なしで完全に同期して調理しました。
ジェーンが夕食を作る方法は次のとおりです。
ジェーンは、(マルチスレッドを使用せずに)非同期クッキングを利用して、ディナーを調理するときに並列処理を実現しました。
サービーがディナーを調理する方法は次のとおりです。
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);
}
}
Task
は、将来の作業を完了するための約束です。使用する場合は、I/O based
の作業に使用できます。これはしないコードの実行に複数のスレッドを使用する必要があります。良い例は、C#5のasync/await
の機能と、HttpClient
を使用してネットワークベースのI/O作業を行うことです。
ただし、TPL
を利用してマルチスレッド処理を行うことができます。たとえば、Task.Run
またはTask.Factory.Startnew
を使用して新しいタスクを開始すると、バックグラウンドでThreadPool
の作業がキューに入れられ、TPL
が抽象化して複数のスレッドを使用できるようになります。
複数のスレッドを使用する一般的なシナリオは、同時に(並行して)実行できるCPUにバインドされた作業がある場合です。マルチスレッドアプリケーションの操作には大きな責任が伴います。
したがって、TPL
を使用することは、必ずしも複数のスレッドを使用することを意味しないことがわかりますが、マルチスレッドを実行するためにそれを確実に活用できます。
Q:しかし、TPLを使用しただけでは、マルチスレッドアプリケーションを作成したことになりますか?
スマートな質問、タスク!=マルチスレッド、 TaskCompletionSource を使用して、Task
を作成し、シングルスレッド(UIスレッドのみの場合もある)自体で実行できます。
Task
は、将来完了する可能性のある操作を抽象化したものです。コードがマルチスレッドであることを意味するものではありません。通常Task
にはマルチスレッドが含まれますが、常にそうであるとは限りません。
また、TPL
の知識があることだけを忘れないでください。マルチスレッドを知っているとは言えません。カバーする必要がある多くの概念があります。
もちろんタスク並列ライブラリも。
注:これは完全なリストではありません。これらは私の頭の上からのものです。
スレッドのみの場合は、 http://www.albahari.com/threading/ をお勧めします
ビデオチュートリアルについては、 Pluralsight をお勧めします。それは支払われますが、コストに見合う価値があります。
最後になりましたが、もちろん、はい Stackoverflow です。
私の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)コードを書いたかどうか尋ねてきたはずです。