5

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ループ?


  • @ I4V:その質問に対する答えは、「すべてのブロッキング操作がasync / awaitモデルを使用して明示的に制御を渡すことが必要である」と述べています。しかし私の質問では、コントロールはProduceConsumeなしでProduce明示的に制御を譲ります。それは私が混乱している部分です。 - Matthew
  • @ MatthewこれはコンソールアプリケーションなのでありませんSynchronizationContextつまり、からのすべてのコールバックがawait電話をするSynchronizationContext.Defaultこれはスレッドプールなので、技術的には実際にはこのプログラムの実行中に実行されている2つのスレッドがあります。追加のスレッドが作成されないようにするには、独自のカスタム同期コンテキストを作成して設定する必要があります。行った場合は、Recieved特にすべてのプロデュースが完了するまで呼び出しは呼び出されません。ではない生産しながら制御を生み出す。 - Servy
  • @Servy:複数のスレッドが関与している場合、MSDNは「特に、競合状態を防ぐ必要がないため、IOに制限された操作に対してBackgroundWorkerよりも優れている」と主張しています。私は自分の例を編集して単純な競合条件を追加しました。 - Matthew
  • @マシューawaitしていません必ずしもスレッドの作成を含みます。スレッドを作成しない方法で使用できます。あなたは単にそうしていないのです。また、コールバックを実行するためだけにスレッドプールthreadを使用していることにも注意してください。そのスレッドプールthreadはありませんブロッキング何もしない場合は、実際には何もしていないため、プールに解放されます。別のリクエストを処理することができます。これは重要なことです。100個のスレッドプールスレッドがそこにブロックされていてIOが完了するのを待っていないという意味です。 - Servy
  • あなたは見つけるかもしれません俺のasync/awaitイントロ役に立った私は単一の記事で(すべての関連する詳細と共に)基本をカバーしようとしました。 - Stephen Cleary

2 답변


2

追加のスレッドが関与していない場合、どのようにしてforループの途中でProduceが歩留まり管理を行うことができますか?

追加のスレッドは必要ないと誰が言ったのですか?あなたが述べた事実は次のとおりです。

asyncキーワードとawaitキーワードによって追加のスレッドが作成されることはありません。

これは絶対に本当です。プログラムにフラグメントが含まれています

target.Post(i);

await source.OutputAvailableAsync())

私の推測では、target.Post(i)またはsource.OutputAvailableAsync()スレッドを作成しました。のawaitスレッドを生成しません。全てのawaitはありますかメソッドの残りの部分を呼び出しによって返されたタスクの続きとして割り当て、呼び出し元に制御を返します。。そのタスクがその仕事をするスレッドを生み出したなら、それはビジネスです。

awaitもう1つの制御フローです。確かに非常に複雑な制御フローですが、それでも制御フローです。それはスレッドを作成するための構文上の砂糖ではありません。タスクに継続を割り当てるための構文上の糖です。


  • 実際、これらの呼び出しはどちらもスレッドを作成しません。 @Servyはその点で正しいです。awaitオペレータ(より具体的には、によって生成されたコードによって使用されるタスク待機者)awaitoperator)はデフォルトを使用していますSynchronizationContextスレッドプールスレッドでメソッドの継続をスケジュールする。 - Stephen Cleary
  • @ステフェンクリアリー:ああ、いいね。メモをありがとう。 - Eric Lippert

-1

制御処理は同じスレッド内で行われるため、ループが実行されると、Consume方法はそうではありません、そしてその逆もあります。スレッドを使用していた場合は、必ずしもそうとは限りません。実際、両方を同時に実行することをお勧めします。

それらが同じスレッド内にあるという事実は、制御がコードの一部から別の部分に渡せないという意味ではありません。 .NET(および他のすべてのフレームワーク、AFAIK)はこれをスムーズに処理し、各部分は問題なく独自のコンテキストで実行されます。

しかし、両方を1つのスレッドで実行するということは、Consume実行中の場合、ループは「ハング」します。もしConsume時間がかかりすぎる、それはまさにユーザーが感じるかもしれないものです。そのため、Windowsフォームに慣れていないプログラマの多くが、一度に大量の情報でGUIコントロールを埋めるとフォームがハングアップして空白になることがあることに驚きます。画面を更新するスレッドは、コントロールロジックが実行されるスレッドと同じです。バックグラウンドワーカースレッドを使用していません。


  • 制御が渡される原因ConsumeなしでProduce明示的に制御を譲る? .NETはそれに気付いていますかProduce長い間実行されてきたConsume(スレッドモデルにおけるOSの役割と同じ) - Matthew
  • 実際、Servyは私の意見よりも良い答えを得たと思います。 - Renan

リンクされた質問


関連する質問

最近の質問