System.Timers Threading Example

Read this article in your language IT | EN | DE | ES


Hack In my last post about the BackgroundWorker object, I showed how to create threads using the toolbox control. I had mentioned an easier way using less code existed and…it involves using a System.Timers.Timer object (and a small but potentially disastrous hack).

This is easy to do and easy to understand for beginners but there are a couple issues with it. From .Net 2 on it is not “legal” to access certain objects on one thread from another thread. This makes sense though if you’ve got separate threads updating something on a main thread, like the thread the Form runs in. This is called being “not thread safe”. To get around this limitation you can set a property…

Control.CheckForIllegalCrossThreadCalls = false;

…which will allow you to access objects across threads. Now, I can see the idealistic developers out there wagging their fingers and saying this is a frowned upon practice. I agree, if we’re talking about more than a couple threads, or an application I would be giving to anyone else. If it:

A. Is a personal utility

B. You only need one or two threads to keep the application responsive

C. The application is very simple

…then go ahead and chunk out a Timer and turn off the checking for illegal cross threads. Those are some pretty stringent limitations that I think are appropriate for the nature of what the application does.

The basis for arguments against it have to do with not being able to know what all your threads are doing. If its a personal utility where you know what your threads are doing and when they are doing it then I think it is a viable option. Although, its kind of like using a butter knife as a screwdriver to save yourself a trip to the garage.

Here is a image of the form, it is just a progress bar and a button:

This first example is showing what not to put in production code. Our CheckForIllegalCrossThreadCalls is set to false on the form load event so that we can update the progress bar from the workTimer_Elapsed method. The Timer itself doesn’t run in a separate thread (or so I’ve read) but evidently the timer tick does. The elapsed event from the Forms thread is raised by delegate from the tick’s thread so the elapsed event runs within the Form’s context (cross thread access). Which is why you have to hack it to set the progress bar status within the elapsed event. Also, if the work that is done in the elapsed event takes longer than the elapsed duration of the timer then any updates will occur when the work finishes, not when the Timer ticks. Just something to keep in mind…

In the code we create a couple class level objects like the timer and some integers. In the button click we set some properties of the Timer object and start it. The timer interval dictates how long the timer runs until it calls the method in the ElapsedEventHandler.

In that method (workTimer_Elapsed) we check to see if the counting integer arbitraryStatus has reached the arbitraryLimit value. If not we increment it, other wise the progress bar gets reset to zero and we stop the Timer. Here’s the example of how not to to do work in a background thread…

BAD EXAMPLE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
namespace TimerThreadingExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        System.Timers.Timer workTimer = new System.Timers.Timer();
        int arbitraryStatus = 0;
        int arbitraryLimit = 10;
        private void button1_Click(object sender, EventArgs e)
        {
            workTimer.Elapsed += new ElapsedEventHandler(workTimer_Elapsed);
            workTimer.Interval = ((1000) * 1);
            workTimer.Start();
        }
        void workTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (arbitraryStatus < arbitraryLimit)
            {
                arbitraryStatus++;
                progressBar1.Value = (10 * arbitraryStatus);
            }
            else
            {
                progressBar1.Value = 0;
                workTimer.Stop();
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //hack, kind of
            Control.CheckForIllegalCrossThreadCalls = false;
        }
    }
}

That’s 51 lines of code including the using references, c# syntax and white space (white space is my friend). Without those it’s right at 13 “written” lines of code. Not bad, we could reduce it even further by setting the interval in the timer declaration and eliminating the arbitraryLimit variable. Since I’m not using all the namespaces most of them could be removed as well.

This next example is showing the same form but using a delegate and Control.BeginInvoke to update the progress bar. This does not require the check for illegal cross threads. The workTimer_Elapsed gets run in a separate thread in which it calls BeginInvoke that uses the Windows API PostMessage to place our event in the applications message queue. Since our event gets placed in the message queue the message pump in the application thread will process it and update the UI. Here’s that example…

GOOD EXAMPLE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
namespace TimerThreadingExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        System.Timers.Timer workTimer = new System.Timers.Timer();
        int arbitraryStatus = 0;
        int arbitraryLimit = 10;
        private void button1_Click(object sender, EventArgs e)
        {
            workTimer.Elapsed += new ElapsedEventHandler(workTimer_Elapsed);
            workTimer.Interval = ((1000) * 1);
            workTimer.Start();
        }
        void workTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (arbitraryStatus < arbitraryLimit)
            {
                arbitraryStatus++;
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new UpdateProgressBarDelegate(UpdateProgressBar), new object[] { arbitraryStatus });
                }
                else 
               {
                    UpdateProgressBar(arbitraryStatus);
                }
            }
            else
            {
                if (this.InvokeRequired) 
               {
                    this.BeginInvoke(new UpdateProgressBarDelegate(UpdateProgressBar), new object[] { 0 });
                }
                else
                {
                    UpdateProgressBar(0); 
               }
                workTimer.Stop();
                workTimer.Dispose();
                workTimer = null;
            }
        }
        private delegate void UpdateProgressBarDelegate(int status); 
       private void UpdateProgressBar(int status) 
       { 
           progressBar1.Value = (10 * status); 
       }
        private void Form1_Load(object sender, EventArgs e)
        {
            //no more hack
        }
    }
}

Here is a link to a CodeProject article on BeginInvoke. Kudos to the guy that wrote it, I’ve found other articles that point to his, other developers think its good too.

http://www.codeproject.com/KB/cs/begininvoke.aspx

So I got to thinking, instead of a Timer why don’t I use a real Thread? (I can almost hear the collective sigh of experienced developers reading this) The difference in the implementation is the thread will start to do work as fast as it can, while the Timer starts to do work as fast as it can at intervals. I decided to show an example with as little code as possible to prevent anything from seeming confusing.

REAL THREAD EXAMPLE

using System;
using System.Windows.Forms;
using System.Threading;
namespace TimerThreadingExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        int arbitraryStatus = 0;
        Thread workThread = null; 
       private void button1_Click(object sender, EventArgs e)
        {
            workThread = new Thread(new ThreadStart(ThreadWorkLoop)); 
           workThread.Start(); 
       }
        private void ThreadWorkLoop()
        {
            while(arbitraryStatus < 10)
            {
                arbitraryStatus++;
                UpdateProgress();
                Thread.Sleep(500);
            }
            //work finished
            arbitraryStatus = 0;
            UpdateProgress();
            workThread.Abort();
        }
        private delegate void UpdateProgressBarDelegate(); 
       private void UpdateProgress()
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new UpdateProgressBarDelegate(UpdateProgress));
            }
            else
            {
                progressBar1.Value = (10 * arbitraryStatus);
            }
        }
    }
}

53 lines total and 16 written lines. Barely more than the bad example above but it requires more knowledge about what to do and how to do it. Working with Threads isn’t as obvious as working with Timers, if for no other reason than the words used to describe the stuff. Hopefully seeing this will be a gentle nudge towards the Threading method and away from Timers.

I was curious to see how many threads my application was using. I also wanted to see it go up and down while the thread was created and destroyed. It turns out Task Manager is perfect for that. With it open, I went to the View menu and choose Select Columns and choose the Threads column. Here’s a screenshot of the application idle (13 threads):

…and here is the Thread doing work…(14 threads)

Clearly a single Thread is being created to do the work. If you’ve built the same thing, try clicking the Run button repeatedly and watch the thread count in the Task Manager and watch what happens to the progress bar. Before you read any further, what do you think is happening…and why?

What is happening is the main reason why threading is hard. If you don’t control what threads are created and get them to all synchronize what they do then you can get too many ants trying to pull the same leaf. The thread count goes up every time you click the Run button and the progress bar goes nut-so. This is because a Thread is created and started in the button click event (see that “new” keyword). By continuing to click it you are spawning off new threads that are all trying to update the progress bar at the same time.

You could disable the Run button while the work was being done but that only protects the application from user action. Technically your application is still open to any of your code so you need to do something specific to limit it.

It turns out Thread.Suspend and Thread.Resume are depreciated so I didn’t want to use those, and I didn’t want to have to change too much to keep it understandable for beginners. This is the change I decided on:

private void button1_Click(object sender, EventArgs e)
{
    if (workThread == null || workThread.ThreadState == ThreadState.Stopped)
    {
        workThread = new Thread(new ThreadStart(ThreadWorkLoop));
        workThread.Start();
    }
}

In our application, the workThread can be null when we first run the app, or the workThread’s ThreadState can be Stopped. If either of those are true then we are ok to create a new thread and start it. The thread gets aborted when the work is done in the ThreadWorkLoop method, which is why the thread count goes down. The ThreadState goes from Abort to Stopped when Abort is called so a check for Stopped will tell us whether we really did get rid of it.

Now for my “Jeff Atwood / CodingHorror” styled rant…

The ThreadState process of “Abort” and “Stopped” seems like really bad nomenclature to me. In my opinion the names that were chosen don’t communicate to humans what is going on making it more confusing than it has to be. “Abort” sounds more terminal than “Stopped” so why does it come before Stopped? The word Stopped indicates something can be started, and it turns out that is not the case, Stopped is dead. Abort sounds like we’re removing all traces of its existence but in reality it is a brief state of the Thread on its way to becoming Stopped. On a long running process you could check for the Abort state of the thread but I’m only updating a progress bar so it happens too fast.

…and why is Suspend and Resume being depreciated? Shouldn’t I have more control over my threads than having to add some wonky code with bad nomenclature? Doesn’t using terms like “Suspend” and “Resume” make sense? Who wrote this garbage? Is it any wonder why the Timer and Control.CheckForIllegalCrossThreadCalls are so attractive? At least I can Start AND Stop a Timer. Why does using threads have to be that different or complex? It’s 2009 after all and threading is not a new concept. Weren’t we supposed to get well packaged stuff out of .Net? Was threading an afterthought?

Alright, I’ll get off my soap box. :)

I thought I’d mention too that I’m not really a WinForm guy. I’ve done a lot more web application stuff so some of this is new to me too. I had to read up on the BeginInvoke stuff. I hope this turns out to be a good introduction for you though. Cheers.

--Robert

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading