1
\$\begingroup\$

I've created a custom timer to satisfy the following requirements:

  • Millisecond accuracy
  • I want the tick event handler to only be called once the current tick handler has completed (much like the winforms timer)
  • I want exceptions on the main UI thread not to be swallowed up by the thread timer so this requires Invoke/Send instead of BeginInvoke/Post

Note: I call timeBeginPeriod(1)/ timeEndPeriod(1) in order to achieve the millisecond accuracy.

I've put in a check if the timer is deleted to make sure that the deletion is complete before the timer can be created again. Does that look ok?

For the IsRunning property, is it possible that racing conditions could occur?

...or is the code a disaster waiting to happen and I should try (again) to get CreateTimerQueueTimer to work?

The code has been tested with a small interval of 1 millisecond and a large interval of 300 milliseconds with frequent start/stops, and I've had no problems so far.

ETA: I've found a problem with it. If the timer is running at an interval of 1 millisecond, and I call, say, Change(300), it locks up @ while (this.DeleteRequest). This must be because the TimerLoop is in the this.CallbackDelegate.Invoke(null) call.

public class MyTimer : IDisposable
{

    private System.Threading.TimerCallback CallbackDelegate;
    private bool DeleteRequest;

    private System.Threading.Thread MainThread;
    public MyTimer(System.Threading.TimerCallback callBack)
    {
        this.CallbackDelegate = callBack;
    }


    public void Create(int interval)
    {
        while (this.DeleteRequest) {
            System.Threading.Thread.Sleep(0);
        }

        if (this.MainThread != null) {
            throw new Exception("");
        }

        this.MainThread = new System.Threading.Thread(TimerLoop);
        // Make sure the thread is automatically killed when the app is closed.
        this.MainThread.IsBackground = true;
        this.MainThread.Start(interval);

    }

    public void Change(int interval)
    {
        // A lock required here?
        if (!this.IsRunning()) {
            throw new Exception("");
        }
        this.Delete();
        this.Create(interval);
    }

    public void Delete()
    {
        this.DeleteRequest = true;
    }

    public bool IsRunning()
    {
        return (this.MainThread != null) && this.MainThread.IsAlive;
    }


    private void TimerLoop(object args)
    {
        int interval = (int)args;
        Stopwatch sw = new Stopwatch();
        sw.Start();


        do {
            if (this.DeleteRequest) {
                this.MainThread = null;
                this.DeleteRequest = false;
                return;
            }

            long t1 = sw.ElapsedMilliseconds;

            // I want to wait until the operation completes, so I use Invoke.
            this.CallbackDelegate.Invoke(null);

            if (this.DeleteRequest) {
                this.MainThread = null;
                this.DeleteRequest = false;
                return;
            }

            long t2 = sw.ElapsedMilliseconds;

            int temp = Convert.ToInt32(Math.Max(interval - (t2 - t1), 0));
            sw.Reset();
            if (temp > 0) {
                System.Threading.Thread.Sleep(temp);
            }

            sw.Start();
        } while (true);

    }

        // dispose calls this.Delete();

}
\$\endgroup\$
1
  • \$\begingroup\$ If you found a solution, please post an answer with it and accept it instead of editing your post. That way future visitors can easily see what your solution was. \$\endgroup\$
    – Adam Lear
    Sep 12 '11 at 17:45
1
\$\begingroup\$

System.Threading.Thread.Sleep() has an accuracy (resolution) of around 15 milliseconds, and so I would stay far away from using that in your code if you need millisecond accuracy. Can you use Stopwatch instead? - this uses a high resolution timer where available, by default. You might find this post on StackOverflow here useful as well.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.