0
votes

I use some System.Threading.Timer(s) in my multithreading application (three such timers actually), but, despite the fact that it's working very well, I have the feeling that I'm doing it wrong and that I consume unnecessary resources. Practically, what I am doing is this :

  1. In the main window of my application (WPF window, but don't think that it matters so much) I open three threads.

  2. in every of those three threads, I initialize and start one of the three timers

The reason that I'm using three different threads to do this is because I remember that I've read somewhere (can't find where) that when I start a System.Threading.Timer I can no longer use the current thread (like the thread would stop somehow at the Timer starting).

Now, I have tested the next simple application and there appears to be no conflict between the Timer and the thread that starts it.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    System.Threading.Timer _t1, _t2, _t3;

    private void Form1_Load(object sender, EventArgs e)
    {
        _t1 = new System.Threading.Timer(new TimerCallback(_t1Run),
                           null, TimeSpan.Zero, TimeSpan.FromMilliseconds(15));
        _t2 = new System.Threading.Timer(new TimerCallback(_t2Run),
               null, TimeSpan.Zero, TimeSpan.FromMilliseconds(35));
        _t3 = new System.Threading.Timer(new TimerCallback(_t3Run),
               null, TimeSpan.Zero, TimeSpan.FromMilliseconds(150));

        for (int i = 1; i <= 5000; i++)
        {
            Console.WriteLine("Writting on main form " + i);
        }
    }

    void _t1Run(object State)
    {
        _t1.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 1");
        _t1.Change(TimeSpan.FromMilliseconds(15), TimeSpan.FromMilliseconds(15));
    }

    void _t2Run(object State)
    {
        _t2.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 2");
        _t2.Change(TimeSpan.FromMilliseconds(35), TimeSpan.FromMilliseconds(35));
    }

    void _t3Run(object State)
    {
        _t3.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
        Console.WriteLine("running 3");
        _t3.Change(TimeSpan.FromMilliseconds(150), TimeSpan.FromMilliseconds(150));
    }

}

So, can I just start all my three Timers on the main window (or I need separate threads) ? Can someone confirm that this causes no problems (because in the application that I've tested it doesn't appear to be any problem) ?

Edit: The three timers have different purposes, and two of them actually have some UI interaction (one interacts with the main window and the other with a secondary window - the third doing just some background operations), but the reason for which I do not use System.Windows.Forms.Timer is that the timers make a lot of operations and if I would do all those operations on the main thread, than I would practically block all the GUI interactions (I call those timer-threads at an interval of 100-200 milliseconds, so just imagine ...) . That's an example of how I do the GUI interactions from the threads called by the Timers:

MainWindow.ThisInstance.TitleLabel.Dispatcher.BeginInvoke((Action)(() => UpdateMainWindow_TimeLabel(TimeLabelValue)));
1

1 Answers

4
votes

You should use System.Windows.Forms.Timer instead of System.Threading.Timer if you have some kind UI element interaction in the Timer callback code.

System.Threading.Timer spins out a threadpool thread whenever the timespan elapsed. But this can create problem with Form elements as they are not thread safe. Accessing UI elements using threads other then the dedicated UI thread can create unwanted result.

To overcome this thread unsafe behavior System.Windows.Forms.Timer should be used. System.Windows.Forms.Timer doesn't create new thread or spin a new threadpool thread. It just run the timer callback in the UI thread. That way all UI interactions are performed in a single thread which ensures thread safty .

But if your timer callback takes significant amount of time to finish then your UI will freeze so System.Windows.Forms.Timer should only be used for very short task. For longer background task you can use System.ComponentModel.BackgroundWorker