2017-09-25 109 views
2

我在後臺每運行x分鐘運行幾個mysql查詢的web服務器應用程序中有一個方法,我搜索了一個很好的方法來完成它,並且遇到了System.Threading.Timer類。我設法編寫內部方法,並使用日誌記錄它的執行情況(每個查詢和每個異常),但是我面臨一個問題,我仍然無法理解發生了什麼,事情是什麼時候該方法需要實際做一些事情(當我在我的應用程序上有一個新記錄時,該方法將獲取它的數據並開始將其寫入我的數據庫,但只會在x分鐘運行數據時纔會執行,否則它將只運行無用)它停止,查詢或線程都沒有異常,我只是檢查日誌,並在x分鐘它應該寫在它上面的東西沒有什麼,只是殺死我的線程,我不明白什麼/爲什麼,如果有人能幫助我我會讚賞(對不起我的英文不好),這裏是代碼片段:System.Threading.Timer內的方法

using (StreamWriter sw = File.AppendText(path + @"\log_test.txt")) { 
       sw.WriteLineAsync("-------- LOG BEGIN --------" + Environment.NewLine); 
       try { 
        var timer = new System.Threading.Timer(async (ev) => { 
         bool sync = await AzureSync.Begin(); 
        }, null, TimeSpan.Zero, TimeSpan.FromMinutes(2)); 
       } 
       catch (Exception ex) { 
        sw.WriteLine(DateTime.Now + "**** ERROR ****" + Environment.NewLine); 
        sw.WriteLine(ex.ToString() + Environment.NewLine); 
        throw ex; 
       } 
      } 
+0

我真的不明白 - 在任何時候都寫了什麼?這個片段,誰執行它?某處,有人必須產生這個代碼。 「LOG BEGIN」行是否寫入? – Onkelborg

+0

考慮循環等待Task.Delay(...)而不是定時器。強迫你考慮停止條件,這很好。 –

+0

你不應該'拋出ex',它會覆蓋堆棧跟蹤。你可以用'throw;'來代替它,當它重新拋出異常時它會保留原始堆棧。 –

回答

2

using語句會過快關閉StreamWriter。實際上,它將在新計時器設置完成後立即關閉StreamWriter。

因此,當定時器以這種方式編碼時,定時器將無法寫入日誌。

您需要將設置爲swusing語句移動到定時器執行的自治方法中。

這應該對你更好。

var timer = new System.Threading.Timer(async (ev) => { 

    using (StreamWriter sw = File.AppendText(path + @"\log_test.txt")) { 
      sw.WriteLineAsync("-------- LOG BEGIN --------" + Environment.NewLine); 
      try { 
       bool sync = await AzureSync.Begin(); 
      } 
      catch (Exception ex) { 
       sw.WriteLine(DateTime.Now + "**** ERROR ****" + Environment.NewLine); 
       sw.WriteLine(ex.ToString() + Environment.NewLine); 
       throw ex; 
      } 
     } 
}, null, TimeSpan.Zero, 120000); //120000 milliseconds is 2 Minutes 

此外,在設置計時器,我不確定TimeSpan.FromMinutes(2)將爲您工作。我相信基於這個代碼示例:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/threading/thread-timers最後一個參數以毫秒爲單位。所以120000是你正在尋找的。

其他需要考慮的事情
雖然上面的代碼將「工作」,它從@Mukesh逃避在他的回答/建議一個嚴重的問題困擾。如果後臺進程拋出異常,它將取消IIS應用程序池進程並強制Web應用程序進行回收。這幾乎是不可取的。上面代碼的寫法是這樣的,定時器proc中的任何異常都會導致這種情況發生,因爲在捕獲異常並記錄它們之後,它會重新拋出它們。認真思考應考慮使用第三方後臺調度,如他的答案/建議提及,或者至少是它會是很好的確保沒有異常從定時器PROC像這樣逃脫:

var timer = new System.Threading.Timer(async (ev) => { 
    try{ 
      using (StreamWriter sw = File.AppendText(path + @"\log_test.txt")) { 
       sw.WriteLineAsync("-------- LOG BEGIN --------" + Environment.NewLine); 
       try { 
        bool sync = await AzureSync.Begin(); 
       } 
       catch (Exception ex) { 
        sw.WriteLine(DateTime.Now + "**** ERROR ****" + Environment.NewLine); 
        sw.WriteLine(ex.ToString() + Environment.NewLine); 
       } 
      } 
     }catch{}; //swallow exception and prevent it from taking down the process/IIS app pool 
}, null, TimeSpan.Zero, 120000); //120000 milliseconds is 2 Minutes 
+0

@OfirWinegarten你是完全正確的。在一個簡短的視圖中,我假定他試圖抓住AzureSync.Begin()調用,因爲設置時間不太可能會拋出。但你是對的,現在他已經編碼了。所以我的答案可能沒有多大用處。我會留給他看,但不要刪除它,以防萬一它爲了嘗試趕上Azure電話而對他有價值。 –

+0

定時器不使用流,這是正確的,但定時器在回調中執行的代碼使用流。如果流在回調被執行之前被放置(原始代碼就是這種情況),那麼就不會有任何東西被寫入該流。 – Onkelborg

+0

我將在這裏運行一些測試,並將這些更改報告回來,這很奇怪,因爲當我開始添加數據以便AzureSync.Begin()實際上會執行某些操作時,整個事情就會停止,沒有任何例外..沒有!這很奇怪 – user3533910

1

我我只是給你一個建議:)。 您不應該在Web應用程序中執行後臺任務,在IIS Web服務器中,應用程序可能會進入睡眠模式或工作進程回收期間,您的後臺任務將無法執行。相反,您可以集成庫專門構建以實現後臺作業,例如Hangfire,FLUENTSCHEDULER等,它們也具有相當不錯的儀表板功能。

請參閱以下關於此事的偉大文章。 How to run Background Tasks in ASP.NET

+0

實際上使用IIS web服務器,我甚至不明白爲什麼線程只是停止沒有任何日誌顯示原因,我會閱讀你的鏈接,非常感謝你 – user3533910

+0

你會友善地幫助我嗎?我嘗試使用hangfire IO並看到了一些我需要做的示例,我有: RecurringJob.AddOrUpdate(「123xyz」,()=> CloudSync(),「*/15 * * *」); 從我讀的這應該每15分鐘觸發CloudSync(),但我不斷收到一個異常「'值不能爲空。參數名稱:方​​法'」但每個教程我讀它是相同的語法 – user3533910

+0

您應該使用'Cron '助手類生成cron表達式,這樣可以最大限度地減少出錯機會。請根據您的要求使用此表達式。 =>「RecurringJob.AddOrUpdate(」123xyz「,()=> CloudSync(),Cron.MinuteInterval(15));」,我用.NET Core和HangFire v 1.6.17測試了這個表達式。 – Mukesh