简体   繁体   中英

Method inside System.Threading.Timer

I have a method in a web server application that runs a few mysql queries every x minutes in the background, I searched a good way to do it and came across the System.Threading.Timer class. I managed to code the method inside and using a log I'm keeping track of it's execution (each query and each exception) but I'm facing a problem that I still cannot understand what's happening, the thing is when the method needs to actually do something (when I have a new record on my application the method will grab it's data and start writing it on my database but it will only do that if at that x minute it runs the data is there otherwise it will just run doing nothing) it stops, there is no exception on either the queries or the thread, I just check the log and at the x minute it should be writing something on it there is simply nothing, something just kills my thread and I can't understand what/why, if anyone can help me I would appreciante (sorry my bad english), here is the code snippet:

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

The using statement will close the StreamWriter too quickly. In fact it will close the StreamWriter right after after the new timer is setup.

So the timer won't be able to write to the log when the timer fires with it coded this way.

You will need to move the using statement that setups up sw into the autonomous method the timer executes.

This should work better for you.

 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

Also in setting up the timer I'm not sure that TimeSpan.FromMinutes(2) will work for you. I believe based on this code example: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/threading/thread-timers that the last parameter is in milliseconds. So 120000 is what you are looking for.


While the above code will "work" it suffers from a serious issue which @Mukesh eludes to in his answer/suggestion. If a background process throws an exception it will take down the IIS app pool process and force the web app to recycle. This is almost never desirable. And the way the above code is written is such that any exception in the timer proc is gonna cause that to happen since after catching an exception and logging them, it rethrows them. Serious thought should be given to using a 3rd party background scheduler as mentioned in his answer/suggestion, or at the very least it'd be good ensure that no exception escapes from the timer proc like so:

 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

I am just giving you a advice :). You should not do background job in web application, In IIS web server, application might go into sleep mode or during worker process recycling your background task will not get executed. Instead of this you can integrate library build specifically to achieve background jobs such as Hangfire , FLUENTSCHEDULER etc. which comes with pretty good dashboard functionality too.

Please see below great article for that matter. How to run Background Tasks in ASP.NET

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM