2017-06-09 28 views
0

我試圖在我的MVC應用程序中生成特定表單的PDF副本。由於這非常耗時,而且客戶端無需等待這一代發生,我試圖通過一系列消防和忘記任務來觸發這一點。從MVC控制器啓動時,Task.Run偶爾會失去安靜

有一點需要注意的是,我需要建立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 
     } 
    } 

內,我寫的MVC格式的HTML內容有問題的字符串輸出到HTML文件,交給的路徑文件到PDF編寫器,生成PDF,然後刪除該HTML文件。

我稱它是控制器POST方法裏面,像這樣:

Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, 
    serverURL, signature, document, HttpContext.ApplicationInstance.Context)); 

該命令被稱爲一個foreach循環構造形式,它們加載到字符串格式的一部分,然後,進他們成爲一項任務。我以防萬一奇怪的是怎麼回事用Task.Run與

Task.Factory.StartNew 

也試過,但沒有產生不同的結果。

我遇到的問題是並非所有的任務都會執行每次。如果我在Visual Studio中運行,並逐步完成調試,它每次都能正常工作。但是,當試圖順序生成11個表單時,有時會生成所有這些表單,有時會生成3或4個,有時會生成全部1個表單。我可以找到異常,並且沒有生成的html文件留在我的文件結構中,因爲部分線程中止了線程。

頁面從帖子返回的速度與生成的表單數量之間似乎有微小的相關性。更長的加載時間通常與更多的表單生成相關......但我的印象並不重要。我把它們分離出來,用它們自己的HttpContext拷貝來分離線程,以便攜帶它們並隨身攜帶。一旦啓動,我不認爲原始線程會影響它們。

關於爲什麼我只獲得3次成功的任何想法對於某些嘗試,任何11次都是另一次嘗試,並且沒有例外?

+3

https://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html您目前正在執行「ThreadPool」方法。 –

+0

@ScottChamberlain這似乎與應用程序回收有關。每當我提交生成這些表單的頁面時,我的問題就會發生,並生成隨機數字的這些表單。 ThreadPool方法在響應來自客戶端的請求後是否與單獨的請求處理程序線程關閉相同? – guildsbounty

+0

您的線程正在被暫停,因爲它是ASP.NET線程池的默認行爲。使用穩定的服務排隊一些工作,不要直接在ASP.NET中這樣做 – VMAtm

回答

0

我會嘗試使用await Task.WhenAll(task1, task2, task3, etc),因爲在所有任務完成之前,您的應用程序可能會關閉。

+0

這不會破壞使用Fire and Forget任務的目的嗎?在將視圖返回給用戶之前,我不想等待任務完成。我希望用戶能夠繼續進行其他工作,而PDF將繼續在後臺生成。 – guildsbounty

+1

@guildsbounty你不能做火,忘了那樣。使用傳統的分佈式處理技術(消息隊列)或類似Hangfire的東西。 – Crowcoder

0
Task.Run(() => AsyncPDFFormGeneration(htmlOutput, serverRelativePath, 
serverURL, signature, document, HttpContext.ApplicationInstance.Context)); 

您對此行有微妙的競爭條件。問題在於HttpContext.ApplicationInstance.Context屬性。任務開始時將進行評估。如果它發生在請求結束之前,這很好。但如果由於某種原因任務需要一點時間才能啓動,那麼請求將首先完成,並且HttpContext將爲空。因此,你會得到一個空引用異常,給你的印象是任務沒有開始(實際上,它確實是在你的try/catch之外崩潰了)。

爲了避免這種情況,只是存儲上下文中的局部變量,並將其用於Task.Run

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是requres System.Web.HttpContext.Current進行設置,但是對於一個隨時忘卻的任務來說,這似乎是一個非常糟糕的選擇。即使你在本地保存了HttpContext,它仍然會被清理乾淨,所以我不確定它會如預期那樣行事。另外,正如在評論中提到的那樣,在ASP.NET上啓動「即燃即用」任務是很危險的。您應該使用HostingEnvironment.QueueBackgroundWorkItem