Click here to Skip to main content
15,886,833 members
Articles / Programming Languages / C# 4.0
Tip/Trick

Sliding Timer

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
10 Jan 2014CPOL2 min read 13.1K   199   7   3
How to run an action after a quiet period

Introduction

This tip shows how you can schedule an action to be run after a quiet period in case you are listening to an event which is raised frequently.

Background

I had this problem recently when developing a plugin. Our code was tracking changes in an object graph and doing some calculations based on the new state. The problem was that the event was raised in batches as the changes were cascaded through the graph. To prevent unnecessary re-calculations, we decided to wait until change propagates through the graph and all change events are fired. Only after a quiet period, the handler is invoked.

One way to do it is to use a timer and then stop and restart it each time a notification arrives. When looking at the implementation of System.Timers.Timer and System.Threading.Timer (the former uses the latter internally), I’ve got the impression that a timer is too heavyweight for this task. Also to make timer stop reliably requires further effort. But the same can be achieved simply by using a lock, a thread and Monitor class.

The Code

We create a class that will act as a timer with the ability to restart the countdown when notification arrives. Let’s assume the following declarations.

C#
private int _timeout; // initialized in constructor
private object _lock = new Object();
private bool _isRunning = false;
public event EventHandler TimeoutElapsed;

On each change, we will call a method that will start or restart the timer respectively.

C#
public void Ping()
{
    lock (_lock)
    {
        if (_isRunning)
        {
            Monitor.Pulse(_lock);
        }
        else
        {
            _isRunning = true;
            Task.Factory.StartNew(WaitForTimeout);
        }
    }
}

When timer is already started, pulse the lock to reset the timer. Otherwise, start a background thread that will wait until timeout expires.

C#
private void WaitForTimeout()
{
    lock (_lock)
    {
        while (Monitor.Wait(_lock, _timeout)) ;
        _isRunning = false;
    }

    RaiseTimeoutElapsed();
}

The background thread waits on the lock for a specified time. If the lock is pulsed and the thread awakes before timeout has expired, it simply goes to sleep again. Otherwise if the thread is not awakened prematurely and timeout expires, it raises an event.

C#
private void RaiseTimeoutElapsed()
{
    var handler = TimeoutElapsed;
    if (handler != null)
    {
        handler(this, EventArgs.Empty);
    } 
}

Points of Interest

This is not reliable in the sense that there is no guarantee that the gap between events in the same logical batch will not exceed the given timeout. It’s up to you to choose proper timeout value.

If you plan to update UI in the callback, don't forget that it is fired on background thread. You need to marshal the update to the main thread.

If there is an exception thrown in the handler, it will be confined to the background thread and eventually end up in TaskScheduler.UnobservedTaskException. You may want to add some exception handling code to RaiseTimeoutElapsed() method as well.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Czech Republic Czech Republic
I started programming in Basic in 1989, learned Pascal in 1993, switched to C/C++ in 1997, discovered Java in 2001 and finally settled with C#/.NET in 2003.

Comments and Discussions

 
QuestionHow can I cancel the Monitor.Wait if I want to stop the timer? Pin
Member 1282782313-Apr-21 6:10
Member 1282782313-Apr-21 6:10 
AnswerRe: How can I cancel the Monitor.Wait if I want to stop the timer? Pin
Member 1282782313-Apr-21 6:14
Member 1282782313-Apr-21 6:14 
Generalit is useful Pin
Southmountain10-Jan-14 5:34
Southmountain10-Jan-14 5:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.