17

スレッドを追加したいときにASP.NETアプリケーションを作成し始めてから、ASP.NETアプリケーション内でスレッドを実行する簡単な方法が3つあります。

  • を使用しSystem.Threading.ThreadPoolます。
  • カスタムデリゲートを使用し、そのBeginInvokeメソッドを呼び出します。
  • System.Threading.Threadクラスを利用してカスタムスレッドを使用する。

最初の2つの方法は、アプリケーションのワーカースレッドをすばやく起動する方法を提供します。ただし、残念ながら、 ASP.NETがHTTP要求を処理するために使用するのと同じプールからのスレッドを消費するため、アプリケーションの全体的なパフォーマンスが低下します。

次に、新しいタスクまたはasync/awaitを使用して書き込みを行いたいと思いましたIHttpAsyncHandler。あなたが見つけることができる1つの例はここでドリューマーシュが説明するものです:https ://stackoverflow.com/a/6389323/261950

My guess is that using Task or async/await still consume the thread from the ASP.NET thread pool and I don't want for the obvious reason.

Could you please tell me if I can use Task (async/await) on the background thread like with System.Threading.Thread class and not from thread pool ?

Thanks in advance for your help.

Thomas

4

6 に答える 6

20

この状況こそ、Taskasync、そしてawait本当に輝くところです。以下は、同じ例を最大限に活用するためにリファクタリングしたものです(マッピング コードをクリーンアップするために、私のAsyncExasyncライブラリのヘルパー クラスもいくつか使用しています)。

// First, a base class that takes care of the Task -> IAsyncResult mapping.
// In .NET 4.5, you would use HttpTaskAsyncHandler instead.
public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler
{
    public abstract Task ProcessRequestAsync(HttpContext context);

    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        var task = ProcessRequestAsync(context);
        return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);
    }

    void EndProcessRequest(IAsyncResult result)
    {
        Nito.AsyncEx.AsyncFactory.ToEnd(result);
    }

    void ProcessRequest(HttpContext context)
    {
        EndProcessRequest(BeginProcessRequest(context, null, null));
    }

    public virtual bool IsReusable
    {
        get { return true; }
    }
}

// Now, our (async) Task implementation
public class MyAsyncHandler : HttpAsyncHandlerBase
{
    public override async Task ProcessRequestAsync(HttpContext context)
    {
        using (var webClient = new WebClient())
        {
            var data = await webClient.DownloadDataTaskAsync("http://my resource");
            context.Response.ContentType = "text/xml";
            context.Response.OutputStream.Write(data, 0, data.Length);
        }
    }
}

(コードに示されているように、.NET 4.5 には上記HttpTaskAsyncHandlerと同様の がありますHttpAsyncHandlerBase)。

素晴らしい点は、バックグラウンド操作の実行中にスレッドを使用しないことですasync

  • ASP.NET 要求スレッドが要求を開始し、WebClient.
  • ダウンロードの進行中、 はawait実際にメソッドから戻りasync、リクエスト スレッドを離れます。そのリクエスト スレッドはスレッド プールに戻され、0 (ゼロ) スレッドがこのリクエストにサービスを提供します。
  • ダウンロードが完了するとasync、リクエスト スレッドでメソッドが再開されます。その要求スレッドは、実際の応答を書き込むためだけに簡単に使用されます。

これが最適なスレッド ソリューションです (応答を書き込むには要求スレッドが必要なため)。

元の例でもスレッドが最適に使用されています。スレッドに関する限り、asyncベースのコードと同じです。しかし、IMOasyncコードは読みやすいです。

について詳しく知りたい場合はasync、私のブログに紹介記事があります。

于 2012-02-10T14:54:32.933 に答える
7

数日前からインターネットで情報を探しています。私が今まで見つけたことを要約しましょう:

ASP.NET ThreadPool の事実

  • Andres が言ったように: async/await が追加の ThreadPool スレッドを消費しないのはいつですか? BCL Async メソッドを使用している場合のみ。IOCP スレッドを使用して IO バウンド操作を実行します。

  • Andres は続けます...同期コードまたは独自のライブラリ コードを非同期実行しようとしている場合、IOCP ThreadPool または独自の ThreadPool を明示的に使用しない限り、そのコードはおそらく追加の ThreadPool スレッドを使用します。

しかし、私が知る限り、IOCP スレッドを使用したいものを選択することはできず、threadPool を正しく実装することは努力する価値がありません。誰かがすでに存在するより良いものを作るとは思えません。

  • ASP.NET は、共通言語ランタイム (CLR) スレッド プールのスレッドを使用して要求を処理します。スレッド プールに使用可能なスレッドがある限り、ASP.NET は着信要求を問題なくディスパッチします。

  • Asyncdelegatesは ThreadPool のスレッドを使用します。

いつ非同期実行の実装について考え始める必要がありますか?

  • アプリケーションが比較的長い I/O 操作 (データベース クエリ、Web サービス呼び出し、およびその他の I/O 操作) を実行する場合

  • I/O 作業を行う場合は、I/O スレッド (I/O 完了ポート) を使用する必要があります。具体的には、使用しているライブラリ クラスでサポートされている非同期コールバックを使用する必要があります。彼らの名前は単語Beginとで始まりますEnd

  • リクエストの処理コストが低い場合、並列処理はおそらく不要なオーバーヘッドです。

  • 着信要求率が高い場合、並列処理を追加してもメリットはほとんどなく、実際にはパフォーマンスが低下する可能性があります。これは、着信作業率が CPU をビジー状態に保つのに十分なほど高い可能性があるためです。

新しいスレッドを作成する必要がありますか?

  • ペストを避けるように、新しいスレッドを作成しないでください。

  • ASP.NET がさらに要求を処理するのを防ぐのに十分な量の作業項目を実際にキューに入れている場合は、スレッド プールが不足しているはずです。文字通り何百もの CPU 集中型の操作を同時に実行している場合、マシンが既に過負荷になっているときに、ASP.NET 要求を処理する別のワーカー スレッドがあれば、どのようなメリットがありますか。

そしてTPL?

  • TPL は、プロセス内で使用可能なリソースを使用するように適応できます。サーバーが既にロードされている場合、TPL はワーカーを 1 つしか使用せず、進行を進めることができます。サーバーがほとんど空いている場合は、ThreadPool が確保できる限り多くのワーカーを使用するようにサーバーを拡張できます。

  • タスクはスレッドプール スレッドを使用して実行します。

参考文献

于 2012-02-24T10:19:22.280 に答える
3

「0 (ゼロ) スレッドがこのリクエストにサービスを提供する」ということは、完全に正確ではありません。「ASP.NET ThreadPool から」という意味だと思いますが、一般的には正しいでしょう。

async/await が追加の ThreadPool スレッドを消費しないのはいつですか? IOCP スレッドを使用して IO バインド操作を実行する BCL 非同期メソッド (WebClient 非同期拡張機能によって提供されるメソッドなど) を使用している場合のみ。

同期コードまたは独自のライブラリ コードを非同期実行しようとしている場合、明示的に IOCP ThreadPool または独自の ThreadPool を使用しない限り、そのコードはおそらく追加の ThreadPool スレッドを使用します。

ありがとう、アンドレス。

于 2012-02-15T16:43:48.377 に答える
1

考慮すべきもう 1 つのことは、async/await と TPL (タスク) は同じものではないということです。

この優れた投稿を読んでください http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx async/await が「バックグラウンド スレッドの使用」を意味しない理由を理解する。

ここでのトピックに戻ると、AsyncHandler 内で高価な計算を実行する特定のケースでは、次の 3 つの選択肢があります。

1) コードを Asynchandler 内に残して、コストのかかる計算で ThreadPool の現在のスレッドを使用するようにします。2) Task.Run または Delegate を使用して、別の ThreadPool スレッドで高価な計算コードを実行します。3) カスタム スレッド プール (または IOCP threadPool) から別のスレッドで高価な計算コードを実行します。

「計算」プロセスの実行時間と負荷の量によっては、2 番目のケースで十分な場合があります。安全なオプションは #3 ですが、コーディングとテストのコストがはるかに高くなります。また、.NET 3.5 にはいくつかのハード制限があるため、非同期設計を使用する運用システムには常に .NET 4 を使用することをお勧めします。

于 2012-02-16T14:56:47.433 に答える
1

Parallel Extensions チームは、 TPL と ASP.NET がどのように ASP.NET ThreadPool を使用するかを説明する ASP.NET での TPL の使用に関するブログ記事を公開しています。この投稿には、適切なアプローチを選択するのに役立つ意思決定チャートも含まれています.

つまり、PLINQ はクエリの実行全体でスレッドプールからコアごとに 1 つのワーカー スレッドを使用するため、トラフィックが多い場合に問題が発生する可能性があります。

一方、Task メソッドと Parallel メソッドは、プロセスのリソースに適応し、処理に 1 つのスレッドしか使用できません。

Async CTP に関する限り、async/await コンストラクトとタスクを直接使用することの間に概念的な違いはほとんどありません。コンパイラは、バックグラウンドで待機をタスクと継続に変換する魔法を使用します。大きな違いは、コードがはるかにクリーンでデバッグしやすいことです。

于 2012-02-16T14:39:55.970 に答える
1

There's a nice implementation of HttpTaskAsyncHandler for the .NET 4.0 in the SignalR project. You may want to ckeck it out: http://bit.ly/Jfy2s9

于 2012-04-21T12:20:32.050 に答える