1

I have a .NET 5.0 Console project.

On start of my program it starts a timer:

public class ApiBase : IHostedService
{
   public static System.Threading.Timer mailScanTimer;

   private void OnStarted()
   {
       Console.WriteLine("Started...");
       Logger.Log("Started...");   

       mailScanTimer = new System.Threading.Timer(
           MailScanTask.MailScanTimerLine, null, 5, Timeout.Infinite);
   }
}

And this is the function the timer calls:

public async static void MailScanTimerLine(object state)
{
     await Task.Run(GetTicketEmailData);

     await Task.Run(GetMailData);

     Thread.Sleep(5000);
     ApiBase.mailScanTimer.Change(0, Timeout.Infinite);
}

The program runs on a Linux server and after 2 to 3 weeks it suddenly stop work.

Is there something wrong with the code? Does someone have any tips to make it better?

Thanks in advance!

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Timebreaker900
  • 312
  • 2
  • 7
  • 21
  • 1
    Are you really invoking the `MailScanTask.MailScanTimerLine` method every 5 milliseconds, without protection against overlapping execution? – Theodor Zoulias Sep 05 '22 at 07:58
  • The problem also is that the timer is not aware of async code. – Jeroen van Langen Sep 05 '22 at 08:02
  • 2
    The code has a lot of bugs that have nothing to do with 2 weeks. The timer fires every 5ms, then the callback freezes the thread for 5 seconds, then the potentially 1000 callbacks that fired in that 5 seconds will try to turn the timer into a single-fire timer. – Panagiotis Kanavos Sep 05 '22 at 08:02
  • What are you trying to do with this code? Why not use a 5 s timer? Better yet, why not use a ready-made scheduling library like Coravel or Hangfire? Coravel was built specifically for mail scheduling and comes with features like email templates – Panagiotis Kanavos Sep 05 '22 at 08:03
  • Have you mistaken the units used for the timer interval? They are milliseconds, not seconds. – Matthew Watson Sep 05 '22 at 08:04
  • I thought they are seconds.. should I remove the thread sleep and change it to 5000? – Timebreaker900 Sep 05 '22 at 08:06
  • How can I do it like Theodor described it? With the overlapping executions? – Timebreaker900 Sep 05 '22 at 08:08
  • 1
    You should definitely change the interval from 5 to 5000 (and the first parameter to `Change()` should ALSO be 5000). Then you can get rid of the Sleep. That will protect against overlapping calls because you would be specifying the last parameter for the constructor and the `Change()` method as `Timeout.Infinite` which means the timer won't fire again until the call to `Change()`, no matter how long the `await` calls take. After calling `Change(5000, TimeoutInfinite)` the timer will fire again ONCE after 5000ms. – Matthew Watson Sep 05 '22 at 08:22
  • You might find this question interesting: [Run async method regularly with specified interval](https://stackoverflow.com/questions/30462079/run-async-method-regularly-with-specified-interval) – Theodor Zoulias Sep 05 '22 at 10:14
  • 1
    Calling `Thread.Sleep(5000)` holds the thread for 5 seconds. If you used `await Task.Delay(TimeSpan.FromSeconds(5.0));` then you would allow the thread to go do useful work elsewhere. – Enigmativity Sep 07 '22 at 22:49
  • I got the **similar behavior** on Windows NT; the cause was that the timer uses *milliseconds* which have been stored as `int` value. `int.MaxValue == 2^31` and `2^31` milliseconds are about `24` days. After **~24 days** (slightly more than 3 weeks) timer stoped working – Dmitry Bychenko Sep 30 '22 at 10:19

0 Answers0