しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET 4.5.2 で追加されたバックグラウンド処理 API を使って待つ必要のない非同期処理を丸投げする

.NET Framework 4.5.2 向けのアプリケーションで Google API クライアント周りがコンパイルエラーになって調べていたときに、そういえば ASP.NET に API が追加されたことを思い出したので調べてみました。

ちなみに .NET Framework 4.5.2 で追加された ASP.NET 向けの機能は以下のようになっています。

New APIs for ASP.NET apps. The new HttpResponse.AddOnSendingHeaders and HttpResponseBase.AddOnSendingHeaders methods let you inspect and modify response headers and status code as the response is being flushed to the client app. Consider using these methods instead of the PreSendRequestHeaders and PreSendRequestContent events; they are more efficient and reliable.


The HostingEnvironment.QueueBackgroundWorkItem method lets you schedule small background work items. ASP.NET tracks these items and prevents IIS from abruptly terminating the worker process until all background work items have completed. This method can't be called outside an ASP.NET managed app domain.


The new HttpResponse.HeadersWritten and HttpResponseBase.HeadersWritten properties return Boolean values that indicate whether the response headers have been written. You can use these properties to make sure that calls to APIs such as HttpResponse.StatusCode (which throw exceptions if the headers have been written) will succeed.

What's New in the .NET Framework 4.5, 4.5.1, and 4.5.2

AddOnSendingHeaders とか HeadersWritten をアプリケーションから直接使う場面はそこまで多くない気がしますが、HostingEnvironment に追加された QueueBackgroundWorkItem は利用価値が高いと思います。

既に .NET Web Development and Tools Blog にて紹介されています。すっかり存在を忘れていました。

QueueBackgroundWorkItem to reliably schedule and run background processes in ASP.NET - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

紹介されている通り、メール送信というような結果を await で待つ必要が無い処理の場合には、QueueBackgroundWorkItem を使うことでレスポンスを素早く返しつつ、時間のかかる処理を完了させることが出来るようになります。

とりあえず簡単なサンプルコードを載せておきます。

public ActionResult Index()
{
    // これは良くない -> http://neue.cc/2013/07/02_412.html
    //ProcessAsync(new CancellationToken());

    HostingEnvironment.QueueBackgroundWorkItem(token => ProcessAsync(token));

    return View();
}

private async Task ProcessAsync(CancellationToken cancellationToken)
{
    Debug.WriteLine("executing");

    // 時間のかかる処理のつもり
    await Task.Delay(10000, cancellationToken);

    Debug.WriteLine("executed");
}

QueueBackgroundWorkItem を使って実行されるタスクには実行コンテキストが渡されないので、ConfigureAwait(false) を呼び出した状態と同じになるようです。これだけだと使うメリットが無さそうですが、QueueBackgroundWorkItem で登録したタスクは CancellationToken が渡されるので、タスク内でキャンセル時の処理を組み込んでおくことが出来ます。

そして ASP.NET の AppDomain がシャットダウンされる時に QueueBackgroundWorkItem で追加されたタスクが残っている場合、デフォルトで 90 秒間はシャットダウンを待つようになっています。

挙動を実際に確認しておきます。まずは QueueBackgroundWorkItem を使わずに Task を投げっぱなしで実行し、IIS Express をシャットダウンした場合のログです。

f:id:shiba-yan:20140831125216p:plain

await 後のログが出力されていないので、IIS Express のシャットダウンによって実行中のスレッドも強制的にシャットダウンされたようですね。

今度は QueueBackgroundWorkItem を使って実行した場合のログです。

f:id:shiba-yan:20140831125346p:plain

CancellationToken を Task.Delay に渡しているので、Task.Delay の処理が途中でキャンセルされて TaskCancelledException が投げられています。

最後は QueueBackgroundWorkItem を使い、Task.Delay に CancellationToken を渡さない場合のログです。

f:id:shiba-yan:20140831125910p:plain

CancellationToken を渡さなかったので Task.Delay はキャンセルされることなく実行されましたが、強制的に AppDomain がシャットダウンされることなく、完了後に IIS Express が終了していることがわかります。

ASP.NET 4.5.2 の環境では普通に ThreadPool を使って実行するよりも、QueueBackgroundWorkItem を使った方が安全かつ確実に処理を行えそうです。

Azure Web サイトの .NET Framework が 4.5.2 にアップデートされていた話 - しばやん雑記

.NET Web Development and Tools Blog の記事では Azure Web サイトはまだ対応していないと書いてありますが、既に 7 月の時点で .NET 4.5.2 へのアップデートが行われたので、管理ポータルの .NET Framework のバージョンを V4.5 に変更すれば使えるようになっています。