33

私はasyncここでいくつかの記事を読んでいます:http ://www.asp.net/web-forms/tutorials/aspnet-45/using-asynchronous-methods-in-aspnet-45そして著者は言います:

非同期作業を行う場合、常にスレッドを使用しているとは限りません。たとえば、非同期Webサービス要求を行う場合、ASP.NETはasyncメソッド呼び出しとawaitの間でスレッドを使用しません。

だから私が理解しようとしているのは、async同時実行にスレッドを使用しない場合はどうなるのかということです。「常にスレッドを使用しているわけではない」とはどういう意味ですか?

最初に、スレッドの操作に関して私が知っていることを説明しましょう(簡単な例ですが、もちろん、ここではUIとワーカーの方法論以外のさまざまな状況でスレッドを使用できます)

  1. 入力を受け取り、出力を与えるUIスレッドがあります。
  2. UIスレッドで処理できますが、UIが応答しなくなります。
  3. したがって、ストリーム関連の操作があり、ある種のデータをダウンロードする必要があるとします。
  4. また、ダウンロード中にユーザーが他のことを実行できるようにします。
  5. ファイルをダウンロードしてプログレスバーを変更する新しいワーカースレッドを作成します。
  6. それが行われると、それを行うことは何もないので、スレッドは強制終了されます。
  7. UIスレッドから続行します。

状況に応じてUIスレッドでワーカースレッドを待つこともできますが、その前にファイルのダウンロード中にUIスレッドで他のことを行ってから、ワーカースレッドを待つことができます。

asyncプログラミングも同じではありませんか?そうでない場合、違いは何ですか?asyncしかし、プログラミングがThreadPoolスレッドをプルするために使用することを読みました。

4

3 に答える 3

34

非同期プログラミングにはスレッドは必要ありません。

「非同期」とは、APIが呼び出し元のスレッドをブロックしないことを意味します。ブロックしている別のスレッドがあるという意味ではありません。

まず、UIの例を考えてみましょう。今回は、実際の非同期APIを使用しています。

  1. 入力を受け取り、出力を与えるUIスレッドがあります。
  2. UIスレッドで処理できますが、UIが応答しなくなります。
  3. したがって、ストリーム関連の操作があり、ある種のデータをダウンロードする必要があるとします。
  4. また、ダウンロード中にユーザーが他のことを実行できるようにします。
  5. 非同期APIを使用してファイルをダウンロードします。ワーカースレッドは必要ありません。
  6. 非同期操作は、進行状況をUIスレッドに報告し(進行状況バーを更新します)、完了をUIスレッドに報告します(他のイベントと同様に応答できます)。

これは、関与するスレッドが1つだけ(UIスレッド)でありながら、非同期操作が実行されていることを示しています。複数の非同期操作を開始できますが、それらの操作に関与するスレッドは1つだけです。つまり、スレッドがブロックされることはありません。

async/awaitは、非同期操作を開始してから戻り、その操作が完了したときにメソッドの残りの部分を続行するための非常に優れた構文を提供します。

ASP.NETも同様ですが、メイン/UIスレッドがない点が異なります。代わりに、不完全なリクエストごとに「リクエストコンテキスト」があります。ASP.NETスレッドはスレッドプールから取得され、要求を処理するときに「要求コンテキスト」に入ります。完了すると、「リクエストコンテキスト」を終了し、スレッドプールに戻ります。

ASP.NETは、各要求の不完全な非同期操作を追跡するため、スレッドがスレッドプールに戻ると、その要求に対して進行中の非同期操作があるかどうかを確認します。存在しない場合、リクエストは完了です。

したがって、awaitASP.NETで不完全な非同期操作を行うと、スレッドはそのカウンターをインクリメントして戻ります。ASP.NETは、カウンターがゼロ以外であるために要求が完了していないことを認識しているため、応答を終了しません。スレッドはスレッドプールに戻り、その時点で、その要求を処理しているスレッドはありません。

async非同期操作が完了すると、メソッドの残りの部分が要求コンテキストにスケジュールされます。ASP.NETはハンドラースレッドの1つ(asyncメソッドの前の部分を実行したスレッドと同じである場合とそうでない場合があります)を取得し、カウンターがデクリメントされ、スレッドがasyncメソッドを実行します。

ASP.NETvNextは少し異なります。フレームワーク全体で非同期ハンドラーのサポートが増えています。しかし、一般的な概念は同じです。

詳細については:

于 2012-06-10T15:27:09.513 に答える
6

初めて非同期を見て待っていたとき、それらは非同期プログラミングモデルのC#シンタックスシュガーだったと思います。私は間違っていました、非同期待機はそれ以上のものです。これはまったく新しい非同期パターンのタスクベースの非同期パターンです。http://www.microsoft.com/en-us/download/details.aspx? id=19957は開始するのに適した記事です。TAPを実装するFCLクラスのほとんどは、APMメソッド(BegingXXX()およびEndXXX())を呼び出します。TAPとAMPの2つのコードスナップは次のとおりです。

TAPサンプル:

    static void Main(string[] args)
    {
        GetResponse();
        Console.ReadLine();
    }

    private static async Task<WebResponse> GetResponse()
    {
        var webRequest = WebRequest.Create("http://www.google.com");
        Task<WebResponse> response = webRequest.GetResponseAsync();
        Console.WriteLine(new StreamReader(response.Result.GetResponseStream()).ReadToEnd());
        return response.Result;
    }

APMサンプル:

    static void Main(string[] args)
    {
        var webRequest = WebRequest.Create("http://www.google.com");
        webRequest.BeginGetResponse(EndResponse, webRequest);
        Console.ReadLine();
    }

    static void EndResponse(IAsyncResult result)
    {
        var webRequest = (WebRequest) result.AsyncState;
        var response = webRequest.EndGetResponse(result);
        Console.WriteLine(new StreamReader(response.GetResponseStream()).ReadToEnd());
    }

最後に、GetResponseAsync()がBeginGetResponse()とEndGetResponse()を内部で呼び出すため、これら2つは同じになります。GetResponseAsync()のソースコードを反映すると、次のようなコードが得られます。

task = Task<WebResponse>.Factory.FromAsync(
       new Func<AsyncCallback, object, IAsyncResult>(this.BeginGetResponse), 
       new Func<IAsyncResult, WebResponse>(this.EndGetResponse), null);

APMの場合、BeginXXX()には、タスク(通常はIOの重い操作)が完了したときに呼び出されるコールバックメソッドの引数があります。新しいスレッドを作成して非同期にすると、両方ともすぐにメインスレッドに戻り、両方ともブロックが解除されます。パフォーマンスの面では、ファイルの読み取り、データベース操作、ネットワーク読み取りなどのI / Oバウンド操作を処理するときに、新しいスレッドを作成するとより多くのリソースが必要になります。新しいスレッドを作成することには2つの欠点があります。

  1. あなたが言及した記事のように、メモリコストがあり、CLRは
    スレッドプールの制限です。
  2. コンテキストスイッチが発生します。一方、非同期は手動でスレッドを作成せず、IOバウンド操作が戻ったときにコンテキストスイッチがありません。

違いを理解するのに役立つ写真を次に示します。

ここに画像の説明を入力してください

この図は、MSDNの記事「ASP.NET2.0の非同期ページ」からのものであり、ASP.NET2.0で古い非同期がどのように機能するかについて非常に詳細に説明しています。

非同期プログラミングモデルについては、JeffreyRichterの記事「 CLR非同期プログラミングモデルの実装」から詳細を入手してください。また、27章の彼の著書「CLRviaCsharp3rdEdition」にも詳細があります。

于 2012-06-09T16:12:01.203 に答える
1

Webアプリケーションを実装していて、各クライアント要求がサーバーに着信するときに、データベース要求を行う必要があると想像してみてください。クライアントリクエストが着信すると、スレッドプールスレッドがコードを呼び出します。ここでデータベース要求を同期的に発行すると、スレッドはデータベースが結果で応答するのを待つために無期限にブロックします。この間に別のクライアント要求が着信した場合、スレッドプールは別のスレッドを作成する必要があり、このスレッドは別のデータベース要求を行うときに再びブロックします。ますます多くのクライアント要求が着信するにつれて、ますます多くのスレッドが作成され、これらのスレッドはすべて、データベースが応答するのを待機するのをブロックします。その結果、Webサーバーは、ほとんど使用されていない多くのシステムリソース(スレッドとそのメモリ)を割り当てています。さらに悪いことに、データベースがさまざまな結果で応答すると、スレッドのブロックが解除され、すべてのスレッドが実行を開始します。ただし、実行中のスレッドが多く、CPUコアが比較的少ない可能性があるため、Windowsは頻繁にコンテキストスイッチを実行する必要があり、パフォーマンスがさらに低下します。これは、スケーラブルなアプリケーションを実装する方法ではありません。

ファイルからデータを読み取るために、ReadではなくReadAsyncを呼び出すようになりました。ReadAsyncは、読み取り操作の保留中の完了を表すTaskオブジェクトを内部的に割り当てます。次に、ReadAsyncはWin32のReadFile関数(#1)を呼び出します。ReadFileは、IRPを割り当て、同期シナリオ(#2)の場合と同じように初期化してから、Windowsカーネル(#3)に渡します。WindowsはIRPをハードディスクドライバーのIRPキュー(#4)に追加しますが、スレッドをブロックする代わりに、スレッドがコードに戻ることができるようになりました。スレッドは、ReadAsync(#5、#6、および#7)の呼び出しからすぐに戻ります。もちろん、IRPはまだ処理されているとは限らないため、ReadAsyncの後に、渡されたByte[]のバイトにアクセスしようとするコードを含めることはできません。

于 2014-11-10T16:16:00.970 に答える