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
{
private int howmany ;
private bool aborting = false ;
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
{
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
{
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
{
PIEBALD.Types.ThreadQueue queue =
new PIEBALD.Types.ThreadQueue ( 5 , false ) ;
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 ) ;
string names = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ;
DemoThread[] threads = new DemoThread [ names.Length ] ;
for ( int i = 0 ; i < names.Length ; i++ )
{
threads [ i ] = new DemoThread
(
names.Substring ( i , 1 )
,
System.Threading.ThreadPriority.BelowNormal
,
new System.TimeSpan ( 0 , 0 , 7 )
,
queue
,
(new System.Random()).Next ( 10 , 100 )
) ;
}
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
{
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.