ASP.NET ThreadPool.QueueUserWorkItem Common.Logging到NLog崩溃IIS - Bug还是我?

时间:2012-12-10 19:43:09

标签: c# asp.net-mvc iis crash nlog

我有一个长期运行的异步任务,它是从ASP.NET MVC4网页启动的。控制器方法如下所示:

[HttpPost]
public ActionResult Index(IndexModel model)
{
    if (ModelState.IsValid)
    {
        try
        {
            model.NotificationRecipient = model.NotificationRecipient.Replace(';', ',');
            ImportConfiguration config = new ImportConfiguration()
            {
                BatchId = model.BatchId,
                ReportRecipients = model.NotificationRecipient.Split(',').Select(c => c.Trim())
            };
            System.Threading.ThreadPool.QueueUserWorkItem(foo => LaunchFileImporter(config, this.HttpContext.ApplicationInstance.Context));
            if (model.RunExport) ThreadPool.QueueUserWorkItem(foo => LaunchFileExporter());
            Log.InfoFormat("Queued the ImportProcessor to process invoices.  Send Notification: {0} Email Recipient: {1}",
                model.SendNotification, model.NotificationRecipient);
            TempData["message"] = "The import processor job has been started.";
            //return RedirectToAction("Index", "Home");
        }
        catch (Exception ex)
        {
            Log.Error("Failed to properly queue the invoice import job.", ex);
            ModelState.AddModelError("", ex.Message);
        }
    }

    var dirInfo = new System.IO.DirectoryInfo(dir);
    model.Files = dirInfo.EnumerateFiles("*.xml").OrderBy(x => x.Name.ToLower());

    return View(model);
}

我的LaunchFileImporter方法如下所示:

private void LaunchFileImporter(ImportConfiguration config, System.Web.HttpContext context)
{
    //the semaphore prevents concurrent running of this process, which can cause contention.
    Log.Trace(t => t("submitter semaphore: {0}", (exporter == null) ? "NULL" : "present."));
    submitter.WaitOne();
    try
    {
        Log.Trace(t => t("Context: {0}", context));
        using (var processor = new ImportProcessor(context))
        {
            processor.OnFileProcessed += new InvoiceFileProcessing(InvoiceFileProcessingHandler);
            processor.OnInvoiceProcessed += new InvoiceSubmitted(InvoiceSubmittedHandler);
            processor.Execute(config);
        }
    }
    catch (Exception ex)
    {
        Log.Error("Failed in execution of the File Importer.", ex);
    }
    submitter.Release();
}

我的记录器是Common.Logging private static readonly ILog,并且是为NLog配置的。它似乎正确连接;至少,我得到了相当数量的原木。

事情就是这样:当我点击System.Threading.ThreadPool.QueueUserWorkItem时,应用程序池死亡会导致无声死亡,重置应用程序池,重新加载成员资格提供程序,重新处理web.config,整个shebang ...... YSOD,网页上没有任何迹象......一切都在悄然爆炸。我得到的最后一个日志条目是Queued the ImportProcessor to process invoices...

我应该注意页面是否刷新。 TempData["message"]被填充并显示在屏幕上,这让我相信问题发生在异步过程中...但几乎立即。由于缺少额外的日志,我假设记录器存在问题。

所以我希望有人可以告诉我发生了什么,指出一些记录在案的问题,告诉我我是一个白痴,或者重现类似bug的东西。

谢谢!

更新

@RichardDeeming指出,上下文信息没有进入生成的线程,这似乎是导致问题的原因。我仍然没有把我的大脑包裹起来为什么这不起作用也没有写下跟踪消息,但是一旦我捕获了我需要的上下文部分IPrincipal,并使用它而不是上下文对象,它只是工作。

1 个答案:

答案 0 :(得分:3)

你会在行中找到NullReferenceException

ThreadPool.QueueUserWorkItem(foo => LaunchFileImporter(config, HttpContext.ApplicationInstance.Context));

请求完成后,HttpContext会被清除。由于异常是在后台线程上引发的,因此它将关闭整个AppDomain,导致应用程序重新启动。

您需要从控制器操作中的上下文中捕获相关状态,并在WaitCallback委托中使用该状态:

IPrincipal user = Context.User;
ThreadPool.QueueUserWorkItem(foo => LaunchFileImporter(config, user));

// Or:
// ThreadPool.QueueUserWorkItem(state => LaunchFileImporter(config, (IPrincipal)state);