Click here to Skip to main content
Licence CPOL
First Posted 6 Dec 2006
Views 24,848
Downloads 98
Bookmarked 40 times

ThreadQueue -- A queue for threads that allows asynchronous execution and a time limit

By | 6 Dec 2006 | Article
Describes my ThreadQueue class and related classes.

Introduction

This article documents my ThreadQueue and ThreadQueueItem classes. Together, these classes and the one (or more) you derive from ThreadQueueItem allow you to easily spin off a number of threads, controlling how many to execute at a time, and killing a thread if it runs too long. And if need be, you can kill all the threads at once. The class you derive from ThreadQueueItem may also include the code to exit the thread neatly rather than have it killed outright.

Background

I use this as part of a Windows service that needs to send data to another system. Each item takes about a minute to send, and I limit it to two minutes. Using this technique, I can have several going at once, kill any that take too long, and if I stop the service, I can kill them all before doing so.

Using the code

The included files are:

  • LogFileIgnoreAttribute.cs
  • ThreadAbortedEventArgs.cs
  • ThreadAbortingEventArgs.cs
  • ThreadCompleteEventArgs.cs
  • ThreadEventArgs.cs
  • ThreadMessageEventArgs.cs
  • ThreadOverdueEventArgs.cs
  • ThreadQueue.cs
  • ThreadQueueDemo.cs
  • ThreadQueueItem.cs
  • ThreadStartingEventArgs.cs
  • ThreadStatus.cs
  • ThreadWaitingEventArgs.cs

Extract them from the zip and add them to your project. (Well, not the demo file, unless you want to build the demo.)

Deriving your own ThreadQueueItem

Here is the definition of DemoThread from the demo:

namespace ThreadQueueDemo
{
    public partial class DemoThread : PIEBALD.Types.ThreadQueueItem
    {
        /* A Really Useful thread will need some data, 
           an int will suffice here */
        private int howmany ;
        
        /* This is how Abort will tell Run to exit neatly */
        private bool aborting = false ;
        
        /* A new constructor to call the base 
           constructor and set the int field */
        public DemoThread
        (
            string                          Name
        ,
            System.Threading.ThreadPriority Priority
        ,
            System.TimeSpan                 TimeLimit
        ,
            PIEBALD.Types.ThreadQueue       Queue
        ,
            int                             HowMany
        ) : base ( Name , Priority , TimeLimit , Queue )
        {
            this.howmany = HowMany ;
            
            return ;
        }
    }
}

You must write an override for Run to do whatever it is that needs to be done. In this case, the Run method is just raising the ThreadMessage event, on the queue to which it belongs, the given number of times, pausing between iterations. Because there's a loop, implementing a scheme to abort the process is easily accomplished with a boolean flag.

namespace ThreadQueueDemo
{
    public partial class DemoThread : PIEBALD.Types.ThreadQueueItem
    {
        /* Run is the method that gets executed by the thread */
        public override void
        Run
        (
        )
        {
            for ( int i = 0 ; !this.aborting && i < this.howmany ; i++ )
            {
                RaiseThreadMessage ( this.Name ) ;
                System.Threading.Thread.Sleep ( 100 ) ;
            }
            
            return ;
        }
    }
}

The Abort method is overridden to toggle the flag and wait for the thread to stop.

namespace ThreadQueueDemo
{
    public partial class DemoThread : PIEBALD.Types.ThreadQueueItem
    {
        /* Abort is the method that tells Run to exit neatly */
        public override void
        Abort
        (
        )
        {
            this.aborting = true ;
            
            while ( this.State == PIEBALD.Types.ThreadStatus.Running )
            {
                System.Threading.Thread.Sleep ( 100 ) ;
            }
            
            base.Abort() ;
            
            return ;
        }
    }
}

Nothing else need be overridden, the base ThreadQueueItem and ThreadQueue will handle the rest.

Running your threads

Here is the definition of Main for ThreadQueueDemo from the demo. It has the burden of instantiating the threads and passing them to the ThreadQueue. All the real work is performed in the background.

namespace ThreadQueueDemo
{
    public partial class
    ThreadQueueDemo
    {
        [System.STAThread]
        public static int
        Main
        (
            string[] args
        )
        {
            try
            {
                /* Step one: Instantiate a ThreadQueue 
                  (a limit of five threads at a time) */
                PIEBALD.Types.ThreadQueue queue = 
                        new PIEBALD.Types.ThreadQueue ( 5 , false ) ;

                /* Step two: Register event handlers (as desired) */
                queue.OnThreadAborted  += new 
                  PIEBALD.Types.Event.ThreadAborted  ( HandleThreadEvent ) ;
                queue.OnThreadAborting += new 
                  PIEBALD.Types.Event.ThreadAborting ( HandleThreadEvent ) ;
                queue.OnThreadComplete += new 
                  PIEBALD.Types.Event.ThreadComplete ( HandleThreadEvent ) ;
                queue.OnThreadMessage  += new 
                  PIEBALD.Types.Event.ThreadMessage  ( HandleThreadEvent ) ;
                queue.OnThreadOverdue  += new 
                  PIEBALD.Types.Event.ThreadOverdue  ( HandleThreadEvent ) ;
                queue.OnThreadStarting += new 
                  PIEBALD.Types.Event.ThreadStarting ( HandleThreadEvent ) ;
                queue.OnThreadWaiting  += new 
                  PIEBALD.Types.Event.ThreadWaiting  ( HandleThreadEvent ) ;
                
                /* Step three: Instantiate and initialize 
                   the threads and populate an array with them */
                /* This demo simply makes twenty-six threads, 
                   each named with a letter                */
                string names = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
            
                DemoThread[] threads = new DemoThread [ names.Length ] ;
                
                for ( int i = 0 ; i < names.Length ; i++ )
                {
                    threads [ i ] = new DemoThread
                    ( 
                        /* The name for the thread */
                        names.Substring ( i , 1 ) 
                    ,
                        /* The priority for the thread */
                        System.Threading.ThreadPriority.BelowNormal
                    ,
                        /* The time limit for the thread (seven seconds) */
                        new System.TimeSpan ( 0 , 0 , 7 )
                    ,
                        /* The queue */
                        queue
                    ,
                        /* Pick a number from 10 to 100 */
                        (new System.Random()).Next ( 10 , 100 )
                    ) ;
                    /* Because the time is limited to seven seconds     */
                    /* and the thread will pause for a 
                       tenth of a second between iterations */
                    /* any thread that is instructed to iterate 
                       seventy or more times is    */
                    /* likely to run over the limit and may get killed  */
                }
                
                /* Step four: Run the threads */
                queue.Run ( threads ) ;
            }
            catch ( System.Exception err )
            {
                System.Console.Write ( err.Message ) ;
            }

            return ( 0 ) ;
        }
    }
}

Handling events

Here is the event handler of ThreadQueueDemo from the demo. It is static in the example, because it is referenced from Main, which is static. All it does is write a message to the console.

namespace ThreadQueueDemo
{
    public partial class
    ThreadQueueDemo
    {
        /* Because all I want to do with the EventArgs 
           is write the ToString() to the console  */
        /* I really only need to write one handler that 
           will handle all seven different events */
        /* (Well I'll treat ThreadMessageEventArgs differently)*/
        public static void
        HandleThreadEvent
        (
            object   sender
        ,
            PIEBALD.Types.Event.ThreadEventArgs e
        )
        {
            if ( e is PIEBALD.Types.Event.ThreadMessageEventArgs )
            {
                System.Console.Write ( e.Thread.Name ) ;
            }
            else
            {
                System.Console.Write ( "<" + e.ToString() + ">" ) ;
            }
            
            return ;
        }
    }
}

Building and running the demo

Here I've extracted the files to their own directory, built the demo with the C# compiler that comes with the .NET 2.0 SDK, and executed the demo.

C:\Projects\CodeProject\Temp>dir
 Volume in drive C has no label.
 Volume Serial Number is 50B2-B107

 Directory of C:\Projects\CodeProject\Temp

2006-12-01  08:25    <DIR>          .
2006-12-01  08:25    <DIR>          ..
2006-11-22  09:59             2,102 LogFileIgnoreAttribute.cs
2006-11-21  11:51             2,325 ThreadAbortedEventArgs.cs
2006-11-21  11:51             2,330 ThreadAbortingEventArgs.cs
2006-11-21  11:51             2,331 ThreadCompleteEventArgs.cs
2006-11-21  11:42             4,157 ThreadEventArgs.cs
2006-11-21  11:51             3,396 ThreadMessageEventArgs.cs
2006-11-21  11:51             2,332 ThreadOverdueEventArgs.cs
2006-11-28  11:13            17,898 ThreadQueue.cs
2006-12-01  08:20             7,287 ThreadQueueDemo.cs
2006-11-22  08:13            14,313 ThreadQueueItem.cs
2006-11-21  11:51             2,329 ThreadStartingEventArgs.cs
2006-11-20  13:35             2,635 ThreadStatus.cs
2006-11-21  11:51             2,332 ThreadWaitingEventArgs.cs
              13 File(s)         65,767 bytes
               2 Dir(s)  165,827,227,648 bytes free

C:\Projects\CodeProject\Temp>csc *.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.


C:\Projects\CodeProject\Temp>ThreadQueueDemo
<Thread A is waiting to start at 2006-12-01 08:49:51>
<Thread B is waiting to start at 2006-12-01 08:49:51>
<Thread C is waiting to start at 2006-12-01 08:49:51>
<Thread D is waiting
to start at 2006-12-01 08:49:51>
<Thread E is waiting to start at 2006-12-01 08:49:51>
<Thread F is waiting to start at 2006-12-01 08:49:51>
<Thread G is waiting to start at 2006-12-0
1 08:49:51><Thread H is waiting to start at 2006-12-01 08:49:51>
<Thread I is waiting to start at 2006-12-01 08:49:51>
<Thread J is waiting to start at 2006-12-01 08:49:51><Thread K
is waiting to start at 2006-12-01 08:49:51>
<Thread L is waiting to start at 2006-12-01 08:49:51>
<Thread M is waiting to start at 2006-12-01 08:49:51>
<Thread N is waiting to start at 2006-12-01 08:49:51>
<Thread O is waiting to start at 2006-12-01 08:49:51>
<Thread P is waiting to start at 2006-12-01 08:49:51>
<Thread Q is waiting to start at 2006-12-01 08:49:51
><Thread R is waiting to start at 2006-12-01 08:49:51>
<Thread S is waiting to start at 2006-12-01 08:49:51>
<Thread T is waiting to start at 2006-12-01 08:49:51>
<Thread U is waiting to start at 2006-12-01 08:49:51>
<Thread V is waiting to start at 2006-12-01 08:49:51>
<Thread W is waiting to start at 2006-12-01 08:49:51>
<Thread X is waiting to start at 2006-12-01 08:49:51>
<Thread Y is waiting to start at 2006-12-01 08:49:51>
<Thread Z is waiting to start at 2006-12-01 08:49:51>
<Thread A is starting at 2006-12-01 08:49:51>
<Thread B is starting at 2006-12-01 08:49:51>AB
<Thread C is starting at 2006-12-01 08:49:51>
<Thread D is starting at 2006-12-01 08:49:51>C
<Thread E is starting at 2006-12-01 08:49:51>DEACDBEDABCEAD
BCEABCDEABCDEABDCEBCDAEABDCEACBDEACBDEABDCEADCBEBDACECBDAE
<Thread A has completed at 2006-12-01 08:49:53>
<Thread F is starting at 2006-12-01 08:49:53>
<Thread B has completed at 2006-12-01 08:49:53>
<Thread G is starting at 2006-12-01 08:49:53>F
<Thread C has completed at 2006-12-01 08:49:53>G
<Thread H is starting at 2006-12-01 08:49:53>
<Thread D has completed at 2006-12-01 08:49:53>
<Thread I is starting at 2006-12-01 08:49:53>H
<Thread E has completed at 2006-12-01 08:49:53>I
<Thread J is starting at 2006-12-01 08:49:53>JGIHJFGIJFHGIFHJGF
IHJGIHJFGHIJFHGIFJJHIGFIGHJFFHGJIIJGFHIJGHFIFGHJIFGHJ
<Thread F has completed at 2006-12-01 08:49:55>
<Thread K is starting at 2006-12-01 08:49:55>K
<Thread G has completed at 2006-12-01 08:49:55>
<Thread L is starting at 2006-12-01 08:49:55>
<Thread H has completed at 2006-12-01 08:49:55>
<Thread M is starting at 2006-12-01 08:49:55>LM
<Thread I has completed at 2006-12-01 08:49:55>
<Thread N is starting at 2006-12-01 08:49:55>
<Thread J has completed at 2006-12-01 08:49:55>
<Thread O is starting at 2006-12-01 08:49:55>
NOMKONLMNOKLMLNKOMKLONMOLNMKLONMKONLLKMONKNMOLKLNMOKNOMLNOMKLOMKLNOMKLN
<Thread K has completed at 2006-12-01 08:49:57>
<Thread P is starting at 2006-12-01 08:49:57>
<Thread L has completed at 2006-12-01 08:49:57>
<Thread Q is starting at 2006-12-01 08:49:57>QP
<Thread M has completed at 2006-12-01 08:49:57>
<Thread R is starting at 2006-12-01 08:49:57>
<Thread N has completed at 2006-12-01 08:49:57>R
<Thread S is starting at 2006-12-01 08:49:57>
<Thread O has completed at 2006-12-01 08:49:57>S
<Thread T is starting at 2006-12-01 08:49:57>
TSTPQRPSQTRPSQRTPSQRTPSQTRSQ
TRPPTSQRPSQTRPSQRTPSQTRPSQTRPSQRTPTSQRPRSQT
<Thread P has completed at 2006-12-01 08:49:58>
<Thread U is starting at 2006-12-01 08:49:58>
<Thread Q has completed at 2006-12-01 08:49:58>
<Thread V is starting at 2006-12-01 08:49:58>VU
<Thread R has completed at 2006-12-01 08:49:58>
<Thread W is starting at 2006-12-01 08:49:58>
<Thread S has completed at 2006-12-01 08:49:58>
<Thread X is starting at 2006-12-01 08:49:58>X
<Thread T has completed at 2006-12-01 08:49:58>
<Thread Y is starting at 2006-12-01 08:49:58>
YWXWVUYYVUWXYXUWVXVYWUUYWVXXUYWVVU
YWXUYWXVVUYWXVUYWXVXUWYVXWYUVWYUXXWYUV
<Thread U has completed at 2006-12-01 08:50:00>
<Thread Z is starting at 2006-12-01 08:50:00>
<Thread V has completed at 2006-12-01 08:50:00>Z<T
hread W has completed at 2006-12-01 08:50:00>
<Thread X has completed at 2006-12-01 08:50:00>
<Thread Y has completed at 2006-12-01 08:50:00>
ZZZZZZZZZZZZZZ<Thread Z has completed at 2006-12-01 08:50:02>
C:\Projects\CodeProject\Temp>

Points of Interest

I hope this at least serves as a decent intro to threading and events.

History

  • First submitted - 2006-12-01.

License

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

About the Author

PIEBALDconsult

Software Developer (Senior)

United States United States

Member

BSCS 1992 Wentworth Institute of Technology
 
Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.
 
OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB
 
---------------
 
"Typing is no substitute for thinking." -- R.W. Hamming
 
"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup
 
ZagNut’s Law: Arrogance is inversely proportional to ability.
 
"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon
 
"linq'ish" sounds like "inept" in German -- Andreas Gieriet
 

"Things would be different if I ran the zoo." -- Dr. Seuss
 
"Wrong is evil, and it must be defeated." – Jeff Ello
 
"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw
 

"Omit needless local variables." -- Strunk... had he taught programming
 
"DON'T BE LIBERAL IN WHAT YOU ACCEPT!"
 
"Software Engineers don't have Trophy Wives; they have Presentation Layers."
 
"We learn more from our mistakes than we do from getting it right the first time."
 
"I'm an old dog and I like old tricks."
 
"Sometimes the envelope pushes back and sometimes you get a really nasty paper cut."
 
"A method shall have one and only one return statement."
 
My first rule of debugging: "If you get a different error message, you're making progress."
 
My golden rule of database management: "Do not unto others' databases as you would not have done unto yours."
 
My general rule of software development: "Design should be top-down, but implementation should be bottom-up."
 
"Today's heresy is tomorrow's dogma."
or
"Today's dogma is yesterday's heresy."
 
"The registry is evil."
 
"Every tool is a hammer."

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Generaldr dobbs Pinmemberpuromtec110:29 23 Sep '09  
Generalinput parameters PinmemberOseke Cilrh21:33 20 May '07  
GeneralRe: input parameters PinmemberPIEBALDconsult13:11 21 May '07  
GeneralRe: input parameters PinmemberOseke Cilrh1:23 24 May '07  
GeneralFormatting Pinmemberbd0ppens2:24 15 Dec '06  
GeneralRe: Formatting PinmemberPIEBALDconsult6:15 18 Dec '06  
GeneralRe: Formatting PinmvpJohn Simmons / outlaw programmer6:19 13 Nov '08  
GeneralRe: Formatting PinmemberPIEBALDconsult12:48 13 Nov '08  
GeneralThreads don't timeout Pinmembercraig.w12:55 7 Dec '06  
GeneralRe: Threads don't timeout PinmemberPIEBALDconsult16:30 7 Dec '06  

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

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 6 Dec 2006
Article Copyright 2006 by PIEBALDconsult
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid