C# 从MVC控制器启动时,Task.Run偶尔会以静默方式失败

C# 从MVC控制器启动时,Task.Run偶尔会以静默方式失败,c#,asp.net-mvc,multithreading,asynchronous,fire-and-forget,C#,Asp.net Mvc,Multithreading,Asynchronous,Fire And Forget,我正在尝试在MVC应用程序中生成特定表单的PDF副本。由于这很耗时,而且客户端不需要等待这一代的发生,因此我尝试将其作为一系列的Fire and Forget任务来触发 需要注意的一个问题是,我需要建立HttpContext,否则我无法修改的一些底层代码将无法工作。我相信我已经解决了这个问题,但我想说出来以防万一 这是我正在调用的函数 private void AsyncPDFFormGeneration(string htmlOutput, string serverRelativeP

我正在尝试在MVC应用程序中生成特定表单的PDF副本。由于这很耗时,而且客户端不需要等待这一代的发生,因此我尝试将其作为一系列的Fire and Forget任务来触发

需要注意的一个问题是,我需要建立HttpContext,否则我无法修改的一些底层代码将无法工作。我相信我已经解决了这个问题,但我想说出来以防万一

这是我正在调用的函数

    private void AsyncPDFFormGeneration(string htmlOutput, string serverRelativePath, string serverURL, string signature, ScannedDocument document, HttpContext httpContext)
    {
        try
        {
            System.Web.HttpContext.Current = httpContext;
            using (StreamWriter stw = new StreamWriter(Server.MapPath(serverRelativePath), false, System.Text.Encoding.Default))
            {
                stw.Write(htmlOutput);
            }

            Doc ABCDoc = new Doc();
            ABCDoc.HtmlOptions.Engine = EngineType.Gecko;
            int DocID = 0;
            DocID = ABCDoc.AddImageUrl(serverURL + serverRelativePath + "?dumb=" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second + DateTime.Now.Millisecond);
            while (true)
            {
                ABCDoc.FrameRect();
                if (!ABCDoc.Chainable(DocID))
                    break;
                ABCDoc.TextStyle.LeftMargin = 100;
                ABCDoc.Page = ABCDoc.AddPage();
                DocID = ABCDoc.AddImageToChain(DocID);
            }//End while (true...

            for (int i = 1; i <= ABCDoc.PageCount; i++)
            {
                ABCDoc.PageNumber = i;
                ABCDoc.Flatten();
            }

            ScannedDocuments.AddScannedDocument(document, ABCDoc.GetData());

            System.IO.File.Delete(Server.MapPath(serverRelativePath));
        }
        catch (Exception e)
        {
            //Exception is logged to the database, and if that fails, to the Event Log
        }
    }
该命令作为foreach循环的一部分调用,foreach循环构造表单,将表单加载为字符串格式,然后将表单传递到任务中。我也试过这个

Task.Factory.StartNew
以防Task.Run出现异常情况,但这不会产生不同的结果

我遇到的问题是,并非所有的任务每次都执行。如果我在VisualStudio中运行并逐步完成调试,它每次都能正常工作。但是,当尝试按顺序生成11个表单时,有时会生成所有表单,有时会生成3或4个表单,有时会生成除1之外的所有表单

我已将错误日志设置为尽可能广泛,但没有抛出我能找到的异常,并且由于线程中途中止,没有生成的html文件留在我的文件结构中

页面从帖子中返回的速度与生成的表单数量之间似乎存在轻微的相关性。加载时间越长,生成的表单就越多……但我觉得这并不重要。我将这些线程与它们自己的HttpContext副本分离,以便随身携带。一旦启动,我不认为原始线程会影响它们


你知道为什么我在一些尝试中只获得了3个成功任务,在另一次尝试中获得了11个成功任务,没有例外吗?

我会尝试使用
等待任务。whalll(task1、task2、task3等)
因为你的应用程序可能在所有任务完成之前关闭。

我会尝试使用
等待任务。whalll(task1、task2、task3等)
因为您的应用程序可能在所有任务完成之前关闭

Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath,
serverURL, signature, document, HttpContext.ApplicationInstance.Context));
你在这条线上有一个微妙的种族条件。问题在于
HttpContext.ApplicationInstance.Context
属性。任务开始时将对其进行评估。如果在请求结束之前发生,则可以。但是,如果出于某种原因,任务需要一点时间来启动,那么请求将首先完成,
HttpContext
将为空。因此,您将有一个空引用异常,给您的印象是任务没有启动(实际上,它没有启动,但在您的try/catch之外立即崩溃)

为了避免这种情况,只需将上下文存储在局部变量中,并将其用于
任务。运行

var context = HttpContext; // Or HttpContext.ApplicationInstance.Context, but I don't really see the point
Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, serverURL, signature, document, context));
也就是说,我不知道您使用的是什么API,需要设置
System.Web.HttpContext.Current
,但对于fire and forget任务来说,这似乎是一个非常糟糕的选择。即使您在本地保存HttpContext,它仍然会被清理,因此我不确定它是否会按预期的方式运行

此外,正如评论中提到的,在ASP.NET上启动fire and forget任务是危险的。你应该改用

你在这条线上有一个微妙的种族条件。问题在于
HttpContext.ApplicationInstance.Context
属性。任务开始时将对其进行评估。如果在请求结束之前发生,则可以。但是,如果出于某种原因,任务需要一点时间来启动,那么请求将首先完成,
HttpContext
将为空。因此,您将有一个空引用异常,给您的印象是任务没有启动(实际上,它没有启动,但在您的try/catch之外立即崩溃)

为了避免这种情况,只需将上下文存储在局部变量中,并将其用于
任务。运行

var context = HttpContext; // Or HttpContext.ApplicationInstance.Context, but I don't really see the point
Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, serverURL, signature, document, context));
也就是说,我不知道您使用的是什么API,需要设置
System.Web.HttpContext.Current
,但对于fire and forget任务来说,这似乎是一个非常糟糕的选择。即使您在本地保存HttpContext,它仍然会被清理,因此我不确定它是否会按预期的方式运行


此外,正如评论中提到的,在ASP.NET上启动fire and forget任务是危险的。您应该改为使用。

您目前正在使用“线程池”方法。@ScottChamberlain,这似乎与应用程序回收有关。每次我提交生成这些表单的页面时,我的问题都会发生,生成这些表单的随机数。线程池方法是否存在与单个请求处理程序线程在响应客户端请求后关闭相同的问题?您的线程将被挂起,因为这是ASP.NET线程池的默认行为。使用稳定服务对某些工作进行排队,不要直接在ASP.NET中这样做。您当前正在使用“线程池”方法。@ScottChamberlain,这似乎与应用程序回收有关。每次我提交生成这些表单的页面时,我的问题都会发生,生成这些表单的随机数。线程池方法是否存在与单个请求处理程序线程在响应客户端请求后关闭相同的问题?您的线程将被挂起,因为这是ASP.NET线程池的默认行为。使用稳定的服务对一些工作进行排队,不要在ASP.NET中直接这样做。这难道不违背使用Fire and Forget任务的目的吗?在将视图返回给用户之前,我不想等待任务完成。我希望用户能够继续进行其他操作,而PDF将继续在后台生成。@guildsbounty您不能这样做。使用传统距离