「ファイア アンド フォーゲット」作業を実行するための一貫した方法は何ですか
ASP.NET は、ファイア アンド フォーゲットの作業用に設計されていません。HTTP リクエストを処理するように設計されています。HTTP 応答が生成されると (アクションが返されると)、その要求/応答サイクルが完了します。
ASP.NET は、アクティブな要求がない場合はいつでも AppDomain を自由に削除できることに注意してください。これは通常、非アクティブ タイムアウトの後、または AppDomain に一定数のガベージ コレクションがある場合、またはまったく理由もなく 29 時間ごとに、共有ホストで行われます。
したがって、「起動して忘れる」ことは本当に必要ありません。応答を生成したいが、ASP.NET にそれを忘れさせたくないのです。willの簡単な解決策はConfigureAwait(false)
、誰もがそれを忘れてしまう原因となります。
このテーマについて詳しく説明しているブログ記事があります。つまり、応答が生成される前に、永続的なレイヤー (Azure テーブルなど) で実行される作業を記録する必要があります。それが理想的なソリューションです。
理想的な解決策を実行しないと、危険な生活を送ることになります。私のブログ投稿にはTask
、ASP.NET ランタイムに s を登録するコードがあります。これにより、応答を早期に返すことができますが、実際にはまだ完了していないことを ASP.NET に通知できます。これにより、未処理の作業中に ASP.NET がサイトを停止するのを防ぐことができますが、ハード ドライブのクラッシュやサーバーの電源コードにつまずくなど、より根本的な障害から保護することはできません。
私のブログ投稿のコードを以下に複製します。AsyncCountdownEvent
それは私のAsyncEx ライブラリに依存します:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Hosting;
using Nito.AsyncEx;
/// <summary>
/// A type that tracks background operations and notifies ASP.NET that they are still in progress.
/// </summary>
public sealed class BackgroundTaskManager : IRegisteredObject
{
/// <summary>
/// A cancellation token that is set when ASP.NET is shutting down the app domain.
/// </summary>
private readonly CancellationTokenSource shutdown;
/// <summary>
/// A countdown event that is incremented each time a task is registered and decremented each time it completes. When it reaches zero, we are ready to shut down the app domain.
/// </summary>
private readonly AsyncCountdownEvent count;
/// <summary>
/// A task that completes after <see cref="count"/> reaches zero and the object has been unregistered.
/// </summary>
private readonly Task done;
private BackgroundTaskManager()
{
// Start the count at 1 and decrement it when ASP.NET notifies us we're shutting down.
shutdown = new CancellationTokenSource();
count = new AsyncCountdownEvent(1);
shutdown.Token.Register(() => count.Signal(), useSynchronizationContext: false);
// Register the object and unregister it when the count reaches zero.
HostingEnvironment.RegisterObject(this);
done = count.WaitAsync().ContinueWith(_ => HostingEnvironment.UnregisterObject(this), TaskContinuationOptions.ExecuteSynchronously);
}
void IRegisteredObject.Stop(bool immediate)
{
shutdown.Cancel();
if (immediate)
done.Wait();
}
/// <summary>
/// Registers a task with the ASP.NET runtime.
/// </summary>
/// <param name="task">The task to register.</param>
private void Register(Task task)
{
count.AddCount();
task.ContinueWith(_ => count.Signal(), TaskContinuationOptions.ExecuteSynchronously);
}
/// <summary>
/// The background task manager for this app domain.
/// </summary>
private static readonly BackgroundTaskManager instance = new BackgroundTaskManager();
/// <summary>
/// Gets a cancellation token that is set when ASP.NET is shutting down the app domain.
/// </summary>
public static CancellationToken Shutdown { get { return instance.shutdown.Token; } }
/// <summary>
/// Executes an <c>async</c> background operation, registering it with ASP.NET.
/// </summary>
/// <param name="operation">The background operation.</param>
public static void Run(Func<Task> operation)
{
instance.Register(Task.Run(operation));
}
/// <summary>
/// Executes a background operation, registering it with ASP.NET.
/// </summary>
/// <param name="operation">The background operation.</param>
public static void Run(Action operation)
{
instance.Register(Task.Run(operation));
}
}
async
または同期コードに対して次のように使用できます。
BackgroundTaskManager.Run(() =>
{
// Synchronous example
Thread.Sleep(20000);
});
BackgroundTaskManager.Run(async () =>
{
// Asynchronous example
await Task.Delay(20000);
});