Web リクエストによって開始される長時間実行プロセスがあります。プロセスが完了するまでの時間を確保するために、新しいスレッドでプロセスをスピンオフし、Mutex を使用してプロセスの 1 つのインスタンスのみが実行されるようにします。このコードは、開発環境とステージング環境では意図したとおりに実行されますが、運用環境では Null 参照例外で失敗します。私たちのアプリケーション ログは何もキャプチャせず、運用担当者は AppPool をクラッシュさせていると報告しています。(環境の問題のようですが、環境が同じように構成されているという前提で進めなければなりません。) これまでのところ、Null 参照がどこにあるかを特定できていません。
アプリケーション イベント ログの例外は次のとおりです。
Exception: System.NullReferenceException
Message: Object reference not set to an instance of an object.
StackTrace: at Jobs.LongRunningJob.DoWork()
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
そして、ここにコードがあります(少しサニタイズされています):
public class LongRunningJob: Job
{
private static Mutex mutex = new Mutex();
protected override void PerformRunJob()
{
var ts = new ThreadStart(LongRunningJob.DoWork);
var thd = new Thread(ts);
thd.IsBackground = true;
thd.Start();
}
private static void DoWork()
{
var commandTimeOut = 180;
var from = DateTime.Now.AddHours(-24);
var to = DateTime.Now;
if (mutex.WaitOne(TimeSpan.Zero))
{
try
{
DoSomethingExternal(); // from what we can tell, this is never called
}
catch (SqlException sqlEx)
{
if (sqlEx.InnerException.Message.Contains("timeout period elapsed"))
{
Logging.LogException(String.Format("Command timeout in LongRunningJob: CommandTimeout: {0}", commandTimeOut), sqlEx);
}
else
{
Logging.LogException(String.Format("SQL exception in LongRunningJob: {0}", sqlEx.InnerException.Message), sqlEx);
}
}
catch (Exception ex)
{
Logging.LogException(String.Format("Error processing data in LongRunningJob: {0}", ex.InnerException.Message), ex);
}
finally
{
mutex.ReleaseMutex();
}
}
else
{
Logging.LogMessage("LongRunningJob is already running.");
}
}
}