Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a C# timer that fires events in the main thread?

Tags:

c#

timer

Long story short, I need a precise timer in .Net - with prescision in milliseconds - meaning, if I tell it to fire an event when 10ms passes, it must do so, +-1ms. The built-in .Net Timer class has a precision of +-16ms it seems, which is unacceptable for my application.

I found this article http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer which provides a code for a timer that is exactly what I need (even more - that has precision in microseconds).

However, problem is, the OnTimer equivalent seems to be executed in another thread. So, if I add some code that does, say:

label1.Text = "Hello World";

I will get an exception, and thus I will have to actually write it like this:

Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));

This is, from what I understand, because the OnTimer event is fired from the timer's thread - where time is passed until enough has passed to be over the Interval, and then next OnTimer event is fired. The .Net Timer does not have such a problem - in OnTimer of the .Net Timer, I can freely modify controls's members.

Question: What should I change so that my timer will run it's OnTimer event in the main thread? Is adding "Invoke" the only choice?

like image 792
Istrebitel Avatar asked Oct 21 '13 15:10

Istrebitel


People also ask

How can I make my own AC at home?

You just need a fan and two plastic soda bottles to make this air conditioner. Modify the bottles by cutting off the end and punching holes in the sides. Then strap them to the back of the fan and fill the bottles with ice. Voila!

How can I make an air conditioner without AC?

Create a cooling cross-breeze by positioning a fan across from an open window. If you're using a window box fan, it should blow into your room at the coolest hours of the day and outward during the warmest hours of the day.


2 Answers

While there are several ways of going about it, the one that I would generally prefer is to have the timer capture the value of SynchronizationContext.Current when it is created. That value will, when in the UI thread, contain the current synchronization context which can be used to execute methods in the message loop when in a UI context. This will work for winforms, WPF, silverlight, etc. All of those paradigms set a synchronization context.

Just grab that value when the timer is created, assuming it's created in the UI thread. If you want have an optional constructor/property to set the value so that you can use it even if the timer isn't created in the UI thread you can, although that shouldn't be needed most of the time.

Then just use the Send method of that context to fire the event:

public class Timer
{
    private SynchronizationContext syncContext;
    public Timer()
    {
        syncContext = SynchronizationContext.Current;
    }

    public event EventHandler Tick;

    private void OnTick()
    {
        syncContext.Send(state =>
        {
            if (Tick != null)
                Tick(this, EventArgs.Empty);
        }, null);
    }

    //TODO other stuff to actually fire the tick event
}
like image 103
Servy Avatar answered Nov 15 '22 20:11

Servy


There's no way around dispatching UI Element access to the main thread. If updating a UI element is really the only thing that you intend to do in the timer callback then forget about about your timer precision requirement. The user won't see the difference between 16ms and 50ms.

Otherwise carry out the time critical work in your timer callback and dispatch the rest of the UI work to the main thread:

void OnTimer()
{
  // time critical stuff here

  Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
}
like image 40
Oliver Weichhold Avatar answered Nov 15 '22 21:11

Oliver Weichhold