开发者

How to prevent System.Timers.Timer from queuing for execution on a thread pool?

There is a problem with standard System.Timers.Timer behaviour. The timer raise Elapsed event with some interval. But when time of execution inside Elapsed event handler exceed timer interval then thread pool begin queuing event handling. This is a problem in my case. This is because with my Elapsed event handler I fetch some data from database and doing something with it and finally save results back to database. But data handling should be provided only once. So, is there a way to prevent from queuing elapse events for System.Timers.Timer.

As illustration for this issue you can consider next test program:

public class EntryPoint
{

    private static void TimeProc(object state, ElapsedEventArgs e)
    {
        Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
        Thread.Sleep(20000);
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Press <Enter> for finishing\n\n");
        ThreadPool.SetMaxThreads(10, 10);
        System.Timers.Timer MyTimer = new System.Timers.Timer(1000);
        MyTimer.Elapsed += new ElapsedEventHandler(TimeProc);
        MyTimer.Start();
        Console.ReadLine();
        MyTimer.Stop();
    }
}

And possible output will be as here:

Current time 03.02.2011 0:00:09 on the thread 4
Current time 03.02.2011 0:00:10 on the thread 5
Current time 03.02.2011 0:00:12 on the thread 6
Current time 03.02.2011 0:00:13 on the thread 7
Current time 03.02.2011 0:00:14 on the thread 8
Current time 03.02.2011 0:00:15 on the thread 9
Current time 03.02.2011 0:00:16 on the thread 10
Current time 03.02.2011 0:00:17 on the thread 11
Current time 03.02.2011 0:00:18 on the thread 12
Current time 03.02.2011 0:00:19 on the thread 13
Current time 03.02.2011 0:00:30 on the thread 4
Current time 03.02.2011 0:00:30 on the thread 5

Possible solutions:

1) It was inspired by:C# Timer vs Thread in Service

And has a code like here regarding to mentioned above sample:

    public class EntryPoint
    {
        private static System.Timers.Timer MyTimer;
        private static void TimeProc(object state, ElapsedEventArgs e)
        {
            Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(20000);
            MyTimer.Enabled = true;
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Press <Enter> for finishing\n\n");
            ThreadPool.SetMaxThreads(10, 10);
            MyTimer = new System.Timers.Timer(1000);
            MyTimer.AutoReset = false;

            MyTimer.Elapsed += new ElapsedEventHandler(TimeProc);
            MyTimer.Enabled = true;
            Console.ReadLine();

        }
    }

2) Second way is about SynchronizingObject, but it is a valuable only for Windows form application or required additional development of code for implementing object that would be implements ISynchronizeInvok开发者_开发知识库e interface. More about this way you can find here

So, for now I will prefer first solution.


What I usually do in this case is stop the timer at the start of the Elapsed handler and start it again at the end. This way, you are only handling one tick at a time.

UPDATE:

Per the MSDN link, I think what they mean is that you can set your own flag (but still have the ticks come in), but thread safety measures should be taken as well.


I would say simply stop it and then start it after your lengthy execution like this.

tmr.Stop();
//Your lengthy execution code goes here
tmr.Start();


The behavior you are seeing is by design. Either set a SynchronizingObject on the timer, or use another timer (such as System.Threading.Timer) that doesn't tick on multiple threads.


I just create a static flag variable. This way my timer keeps running, but the code is simply bypassed if it the method has not completed before the next timer cycle.

In the method used for the timer, test if an event is in progress.

Timer_Method_Called()
{
  if (eventInProgress == 0)
  {
     // flag event as in progress
     eventInProcess == 1;

     // perform code....

     // after code is complete, allow the method to execute
     eventInProgress == 0;
  }
}


Since none of the answers are thread safe, let me propose one that is:

void oneHundredMS_Elapsed(object sender, System.Timers.ElapsedEventArgs e) {

  if (setTimerBodyRunning()) { //only proceed to body if it is not already processing; setTimerBodyRunning must be thread-safe
    // here you do your long running operation
    setTimerBodyFinished();
  }
}

As you can see, the timer handler first checks to see if it is not already running, and only proceeds to the body if false is returned. If true is returned, then the handler quickly returns and ticks do not queue (which they would have had a simple lock statement been used). Here are the definitions for setTimerBodyRunning and setTimerBodyFinished:

private bool setTimerBodyRunning() {
        bool retVal = false;
        lock (timerBodyRunning) { //timerBodyRunning is type object and it holds a bool.  
            //The reason it is object and not bool is so it can be locked on to ensure thread safety
            if (!((bool)timerBodyRunning)) {
                timerBodyRunning = true;
                retVal = true;
            }
        }
        return retVal;
    }

private void setTimerBodyFinished() {
    lock (timerBodyRunning) {
        timerBodyRunning = false;
    }
}

Here's how you'd initialize and start the timer:

object timerBodyRunning = new object();
timerBodyRunning = false;
System.Timers.Timer timerFrequency100MS = new System.Timers.Timer();
timerFrequency100MS.Interval = FREQUENCY_MS; //it will fire every 100 milliseconds
timerFrequency100MS.Elapsed += new System.Timers.ElapsedEventHandler(oneHundredMS_Elapsed);
timerFrequency100MS.Start();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜