2017-09-25 22 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