【问题标题】:Method inside System.Threading.TimerSystem.Threading.Timer 中的方法
【发布时间】:2017-09-25 17:39:50
【问题描述】:

我在 Web 服务器应用程序中有一个方法,它每 x 分钟在后台运行几个 mysql 查询,我搜索了一个很好的方法并遇到了 System.Threading.Timer 类。我设法在内部对方法进行编码并使用日志来跟踪它的执行(每个查询和每个异常),但是我面临一个问题,我仍然无法理解发生了什么,问题是该方法何时需要实际做某事(当我在我的应用程序上有一条新记录时,该方法将获取它的数据并开始将其写入我的数据库,但它只会在它运行的那个 x 分钟内执行此操作,否则它只会运行什么都不做)它停止了,查询或线程都没有例外,我只是检查日志,在 x 分钟它应该在上面写一些东西,根本什么都没有,有些东西只是杀死了我的线程,我不明白是什么/为什么,如果有人可以帮助我,我会很感激(对不起我的英语不好),这里是代码 sn-p:

 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;
                }
            }

【问题讨论】:

  • 我真的不明白 - 任何时候都写过什么吗?这个sn-p,谁来执行?在某个地方,必须有人生成此代码。 “LOG BEGIN”这行写完了吗?
  • 考虑循环等待 Task.Delay(...) 而不是计时器。迫使您考虑停止条件,这很好。
  • 你不应该这样做throw ex,它会覆盖堆栈跟踪。你可以改用throw;,它会在重新抛出异常时保留原始堆栈。

标签: c# asp.net .net multithreading timer


【解决方案1】:

usingstatement 将关闭 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 就是你要找的。​​p>

需要考虑的其他事项
虽然上面的代码会“工作”,但它会遇到一个严重的问题,@Mukesh 在他的回答/建议中回避了这个问题。如果后台进程引发异常,它将关闭 IIS 应用程序池进程并强制 Web 应用程序回收。这几乎是不可取的。上面代码的编写方式是,计时器过程中的任何异常都会导致这种情况发生,因为在捕获异常并记录它们之后,它会重新抛出它们。应该认真考虑使用他的回答/建议中提到的第 3 方后台调度程序,或者至少确保不会像这样从计时器过程中逃脱异常:

 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

【讨论】:

  • @OfirWinegarten 你是完全正确的。快速浏览一下,我假设他正在尝试捕获 AzureSync.Begin() 调用,因为设置时间不太可能会抛出。但你是对的,这就是他现在编码的内容。所以我的回答可能没有多大用处。不过我会把它留给他,而不是删除它,以防万一他想尝试接听 Azure 电话,它对他有价值。
  • 定时器不使用流,没错,但是定时器在回调中执行的代码使用了流。如果在执行回调之前处理流(原始代码中就是这种情况),则不会将任何内容写入该流。
  • 如果AzureSync.Begin调用在原始版本中抛出异常,则根本没有异常处理,但是上面的版本有异常处理。
  • @Onkelborg 我完全同意。我的假设是原始发布者试图为 Azure 调用提供 try catch 支持。我当然想要那个报道:-)
  • 2 分钟是 120000 毫秒,无论如何都有一个接受 TimeSpan - docs.microsoft.com/en-us/dotnet/api/… 的构造函数
【解决方案2】:

我只是给你一个建议:)。 您不应该在 Web 应用程序中执行后台作业,在 IIS Web 服务器中,应用程序可能会进入睡眠模式或在工作进程回收期间您的后台任务将不会被执行。取而代之的是,您可以专门集成库构建以实现后台作业,例如 HangfireFLUENTSCHEDULER 等,这也带有非常好的仪表板功能。

请参阅下面的精彩文章。 How to run Background Tasks in ASP.NET

【讨论】:

  • 实际使用IIS网络服务器,我什至无法理解为什么线程只是停止而没有任何日志显示原因,我会阅读您的链接,非常感谢
  • 你愿意帮助我吗?我正在尝试使用hangfire IO,并看到了一些我需要做的例子,我有: RecurringJob.AddOrUpdate("123xyz", () => CloudSync(), "*/15 * * * *");从我阅读的内容来看,这应该每 15 分钟触发一次 CloudSync(),但我不断收到异常“'Value cannot be null. Parameter name: method'”,但我阅读的每个教程都是相同的语法
  • 您应该使用“Cron”辅助类来生成 cron 表达式,这样可以最大限度地减少出错的机会。请根据您的要求使用此表达式。 => "RecurringJob.AddOrUpdate("123xyz", () => CloudSync(), Cron.MinuteInterval(15));",我已经用 .NET Core 和 HangFire v 1.6.17 测试了这个表达式。
猜你喜欢
  • 1970-01-01
  • 2011-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
相关资源
最近更新 更多