この質問にはすでに答えがあります。
MSDNによると、async
そしてawait
新しいスレッドを作成しないでください。
の
async
そしてawait
キーワードによって追加のスレッドが作成されることはありません。
これを念頭に置いて、私はいくつかの簡単なプログラムの制御フローを理解することが困難です。私の完全な例は以下の通りです。それが必要であることに注意してくださいデータフローライブラリこれはNuGetからインストールできます。
using System;
using System.Threading.Tasks.Dataflow;
namespace TaskSandbox
{
class Program
{
static void Main(string[] args)
{
BufferBlock<int> bufferBlock = new BufferBlock<int>();
Consume(bufferBlock);
Produce(bufferBlock);
Console.ReadLine();
}
static bool touched;
static void Produce(ITargetBlock<int> target)
{
for (int i = 0; i < 5; i++)
{
Console.Error.WriteLine("Producing " + i);
target.Post(i);
Console.Error.WriteLine("Performing intensive computation");
touched = false;
for (int j = 0; j < 100000000; j++)
;
Console.Error.WriteLine("Finished intensive computation. Touched: " + touched);
}
target.Complete();
}
static async void Consume(ISourceBlock<int> source)
{
while (await source.OutputAvailableAsync())
{
touched = true;
int received = source.Receive();
Console.Error.WriteLine("Received " + received);
}
}
}
}
出力:
Producing 0
Performing intensive computation
Received 0
Finished intensive computation. Touched: True
Producing 1
Performing intensive computation
Received 1
Finished intensive computation. Touched: True
Producing 2
Performing intensive computation
Received 2
Finished intensive computation. Touched: False
Producing 3
Performing intensive computation
Received 3
Finished intensive computation. Touched: False
Producing 4
Performing intensive computation
Received 4
Finished intensive computation. Touched: True
これはそれを示しているようですConsume
が制御されている間for
ループが実行されているOutputAvailableAsync
タスクは完了します。
for (int j = 0; j < 100000000; j++)
;
これはスレッドモデルでは当然のことです。追加のスレッドが関与していない場合は、Produce
中央の歩留り管理for
ループ?
追加のスレッドが関与していない場合、どのようにしてforループの途中でProduceが歩留まり管理を行うことができますか?
追加のスレッドは必要ないと誰が言ったのですか?あなたが述べた事実は次のとおりです。
asyncキーワードとawaitキーワードによって追加のスレッドが作成されることはありません。
これは絶対に本当です。プログラムにフラグメントが含まれています
target.Post(i);
await source.OutputAvailableAsync())
私の推測では、target.Post(i)
またはsource.OutputAvailableAsync()
スレッドを作成しました。のawait
スレッドを生成しません。全てのawait
はありますかメソッドの残りの部分を呼び出しによって返されたタスクの続きとして割り当て、呼び出し元に制御を返します。。そのタスクがその仕事をするスレッドを生み出したなら、それはビジネスです。
await
もう1つの制御フローです。確かに非常に複雑な制御フローですが、それでも制御フローです。それはスレッドを作成するための構文上の砂糖ではありません。タスクに継続を割り当てるための構文上の糖です。
await
オペレータ(より具体的には、によって生成されたコードによって使用されるタスク待機者)await
operator)はデフォルトを使用していますSynchronizationContext
スレッドプールスレッドでメソッドの継続をスケジュールする。 - Stephen Cleary
制御処理は同じスレッド内で行われるため、ループが実行されると、Consume
方法はそうではありません、そしてその逆もあります。スレッドを使用していた場合は、必ずしもそうとは限りません。実際、両方を同時に実行することをお勧めします。
それらが同じスレッド内にあるという事実は、制御がコードの一部から別の部分に渡せないという意味ではありません。 .NET(および他のすべてのフレームワーク、AFAIK)はこれをスムーズに処理し、各部分は問題なく独自のコンテキストで実行されます。
しかし、両方を1つのスレッドで実行するということは、Consume
実行中の場合、ループは「ハング」します。もしConsume
時間がかかりすぎる、それはまさにユーザーが感じるかもしれないものです。そのため、Windowsフォームに慣れていないプログラマの多くが、一度に大量の情報でGUIコントロールを埋めるとフォームがハングアップして空白になることがあることに驚きます。画面を更新するスレッドは、コントロールロジックが実行されるスレッドと同じです。バックグラウンドワーカースレッドを使用していません。
Produce
にConsume
なしでProduce
明示的に制御を譲ります。それは私が混乱している部分です。 - MatthewSynchronizationContext
つまり、からのすべてのコールバックがawait
電話をするSynchronizationContext.Default
これはスレッドプールなので、技術的には実際にはこのプログラムの実行中に実行されている2つのスレッドがあります。追加のスレッドが作成されないようにするには、独自のカスタム同期コンテキストを作成して設定する必要があります。行った場合は、Recieved
特にすべてのプロデュースが完了するまで呼び出しは呼び出されません。ではない生産しながら制御を生み出す。 - Servyawait
していません必ずしもスレッドの作成を含みます。スレッドを作成しない方法で使用できます。あなたは単にそうしていないのです。また、コールバックを実行するためだけにスレッドプールthreadを使用していることにも注意してください。そのスレッドプールthreadはありませんブロッキング何もしない場合は、実際には何もしていないため、プールに解放されます。別のリクエストを処理することができます。これは重要なことです。100個のスレッドプールスレッドがそこにブロックされていてIOが完了するのを待っていないという意味です。 - Servyasync
/await
イントロ役に立った私は単一の記事で(すべての関連する詳細と共に)基本をカバーしようとしました。 - Stephen Cleary