Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when timer threads don't finish on time

I am using System.Threading Timers to poll sensors on different threads (one at a time was slow due to communication latency). It also allows the user change the polling rate by changing the timer period.

What I can't work out is what happens if timers don't finish before the next period. I wrote a test program, but it didn't really answer anything.

If I have a function that takes ~1.7s to run and call it every 10s, it finishes before the next one starts, and my CPU usage fluctuates between 13% (one core 100%) and 0%.

t = new Timer(doWork, null, 0, 10000);

private void doWork(object o)
{
    for(int i = 0; i < numberItts; i++)
    {
    }
}

If I then lower the timer period to 1s I would expect it to either not execute a thread until the previous one is finished, or keep spawning new threads and the CPU usage would climb as more threads start before other finish. What actually happens is CPU usage fluctuates between 13% and 25%.

Changing the period to 500ms, the CPU useage then fluctuates between 38% and 50%. Surely at this point they should be starting much faster than they are ending.

How are these threads managed? What is limiting the amount created when the polling rate is faster than the rate at which threads can be finished?

like image 326
MikeS159 Avatar asked Jan 26 '17 10:01

MikeS159


People also ask

Are timers thread safe?

Timer is not thread-safe.

How do you stop a timer thread?

start() is a function that is used to initialize a timer. To end or quit the timer, one must use a cancel() function. Importing the threading class is necessary for one to use the threading class. The calling thread can be suspended for seconds using the function time.

How does system threading timer work?

The application thread creates the timer, which waits one second and then executes the CheckStatus callback method every 250 milliseconds. The application thread then blocks until the AutoResetEvent object is signaled. When the CheckStatus callback method executes maxCount times, it calls the AutoResetEvent.

Does timer start a new thread?

No, a timer runs in the thread in which it was created.

What is the difference between Windows Forms timer and threading timer?

For Example, if a Delay Time is 2000 Milliseconds, then after the Timer creation, it will wait for 2 seconds before calling the Timer Callback. Unlike the Windows Forms’ Timer, the Threading Timer will invoke the Timer Callback in different thread

How to use timer class in Java threading?

The threading class has a subclass called the class timer. In technical terms, we will create Timer objects when we need time-bound actions (methods), in technical terms. To use Timer class we will first have to import the time module. The args parameter is always preferably used to declare arguments to the functions to be called.

Should the timer component belong to the same thread?

Although this may be true, the only constraint is that the Timer Component should belong to the Same UI thread. The Timer Component from the Timer name space if useful when we want to achieve the Mixture of UI and System Tasks.

What are the advantages of threading in programming?

The beauty of threading is that you can tell the computer to perform a task some other time or do it simultaneously. You can also execute the code simultaneously on different threads, making it extremely powerful. A timer class always runs in intervals.


2 Answers

Unlike System.Windows.Forms.Timer, System.Threading.Timer uses a thread pool and is not subject to being blocked if your timer handler takes longer than the timer interval to complete.

So if your doWork takes about "~1.7s" to complete and your timer interval is one second, you would expect to see multiple concurrent threads entering doWork.

How are these threads managed? What is limiting the amount created when the polling rate is faster than the rate at which threads can be finished?

That's all handled by the Timer class and associated thread pool.

MSDN has this to say:

The callback method executed by the timer should be reentrant, because it is called on ThreadPool threads. The callback can be executed simultaneously on two thread pool threads if the timer interval is less than the time required to execute the callback, or if all thread pool threads are in use and the callback is queued multiple times. more...

So given this piece of code, where the timer interval is 2 seconds and handler processing time is 1 second, we can expect the same thread each time because its generally better to re-use the same thread than spin up a new one:

class Program
{
    static void Main(string[] args)
    {
        var t = new Timer(doWork, null, 0, 1000);


        Console.WriteLine("Press any key to quit");
        Console.ReadKey();
    }

    private static void doWork(object o)
    {
        Console.WriteLine("Thread: {0}", Environment.CurrentManagedThreadId);

        // simulate lengthy process
        Thread.Sleep(1000);
    }
}

enter image description here

Changing the interval and processing time to 1 second, leads to random threads due to the slight overlap.

enter image description here

Changing the interval to 200ms and keeping processing time to 1 second results in a higher number of worker threads than before. The reason being is that the thread pool has realised that delegates are taking longer to complete than the timer interval so it tries to keep up:

enter image description here

like image 194
MickyD Avatar answered Nov 15 '22 00:11

MickyD


I think the following happens for a timer period of 1s:

  • at 0 seconds: Thread 1 is started. One processor is busy -> ~13%
  • at 1 second: Thread 2 is started. Two processors are busy -> ~25%
  • at 1.7 seconds: Thread 1 is completed. Only one processor is busy -> ~13%
  • at 2 seconds: Thread 3 is started. Two processors are busy ->~25%
  • at 2.7 seconds: Thread 2 is completed. Only one processor is busy -> ~13%
  • ...

I.e. still the threads are processed faster than they are created. Even for 500ms this will be true.

I think the behaviour you expected will occur when you reduce the timer period to a value < 1.7s/8 = ~0.2125s (Assuming you have 8 processors available doing nothing but process your threads which takes them 1.7s.)

like image 23
wkl Avatar answered Nov 14 '22 23:11

wkl