How to let Timer skip tick if the previous thread is still busy

后端 未结 8 1631
攒了一身酷
攒了一身酷 2020-12-01 11:28

I created a windows service, that is supposed to check a certain table in the db for new rows every 60 seconds. For every new row that was added, I need to do some heavy pro

相关标签:
8条回答
  • 2020-12-01 11:37

    Put a quick check it see if the service is running. if it is running it will skip this event and wait for the next one to fire.

    Timer serviceTimer = new Timer();
    serviceTimer.Interval = 60;
    serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
    serviceTimer.Start();
    bool isRunning = false;
    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        lock (this)
        {
            if(isRunning)
                return;
            isRunning = true;
        }
        try
        {
        // do some heavy processing...
        }
        finally
        {
            isRunning = false;
        }
    }
    
    0 讨论(0)
  • 2020-12-01 11:38

    You don't need the lock in this case. Set timer.AutoReset=false before starting it. Restart the timer in the handler after you are done with your processing. This will ensure that the timer fires 60 seconds after each task.

    0 讨论(0)
  • 2020-12-01 11:39

    I recommend you don't let the timer tick at all while its processing.

    Set the Timers AutoReset to false. And start it at the end. Here's a full answer you might be interested in Needed: A Windows Service That Executes Jobs from a Job Queue in a DB; Wanted: Example Code

    0 讨论(0)
  • 2020-12-01 11:43

    another posibility would be something like this:

    void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
    {   
        if (System.Threading.Monitor.IsLocked(yourLockingObject))
           return;
        else
           lock (yourLockingObject)
           // your logic  
               ;
    }
    
    0 讨论(0)
  • 2020-12-01 11:46

    You might try disabling the timer during processing, something like

    // Just in case someone wants to inherit your class and lock it as well ...
    private static object _padlock = new object();
    try
    {
      serviceTimer.Stop(); 
    
      lock (_padlock)
        { 
            // do some heavy processing... 
        } 
    }
    finally
    {
      serviceTimer.Start(); 
    }
    

    Edit : OP didn't specify whether the reentrancy was caused only by the timer or whether the service was multi threaded. Have assumed the later, but if the former then locking should be unnecessary if the timer is stopped (AutoReset or manually)

    0 讨论(0)
  • 2020-12-01 11:55

    Other options might be to use a BackGroundWorker class, or TheadPool.QueueUserWorkItem.

    Background worker would easily give you the option check for current processing still occurring and process 1 item at a time. The ThreadPool will give you the ability to continue queueing items every tick (if necessary) to background threads.

    From your description, I assume you are checking for items in a queue in a database. In this case, I would use the ThreadPool to push the work to the background, and not slow/stop your checking mechanism.

    For a Service, I would really suggest you look at using the ThreadPool approach. This way, you can check for new items every 60 seconds with your timer, then Queue them up, and let .Net figure out how much to allocate to each item, and just keep pushing the items into the queue.

    For Example: If you just use a timer and you have 5 new rows, which require 65 seconds of processing time total. Using the ThreadPool approach, this would be done in 65 seconds, with 5 background work items. Using the Timer approach, this will take 4+ minutes (the minute you will wait between each row), plus this may cause a back-log of other work that is queueing up.

    Here is an example of how this should be done:

    Timer serviceTimer = new Timer();
        void startTimer()
        {
            serviceTimer.Interval = 60;
            serviceTimer.Elapsed += new ElapsedEventHandler(serviceTimer_Elapsed);
            serviceTimer.AutoReset = false;
            serviceTimer.Start();
        }
        void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                // Get your rows of queued work requests
    
                // Now Push Each Row to Background Thread Processing
                foreach (Row aRow in RowsOfRequests)
                {
                    ThreadPool.QueueUserWorkItem(
                        new WaitCallback(longWorkingCode), 
                        aRow);
                }
            }
            finally
            {
                // Wait Another 60 Seconds and check again
                serviceTimer.Stop();
            }
        }
    
        void longWorkingCode(object workObject)
        {
            Row workRow = workObject as Row;
            if (workRow == null)
                return;
    
            // Do your Long work here on workRow
        }
    
    0 讨论(0)
提交回复
热议问题