Multi threaded design patterns

B

Bill McCormick

Hello,

I'd like to bounce some things off developers and MSDN folks that are here.

I have a Windows service MyService that is the main application. It will
build a MyWorkObject in it's constructor and the OnStart and OnStop
methods set a property that will start/stop a worker thread or threads.
The worker thread(s) will process a queue and the dequeued item will be
processed by some asynchronous process.

Just a few questions for starters:

1. Is the general framework of this OK? That is, the service constructor
creates an object and OnStart/OnStop methods start/stop worker threads?

2. If garbage collection is no panacea, what questions do I need to ask
to determine the disposal process of MyObject and objects that it creates?

3. What threading design should I use to handle the
myWorkQueueX.Process() so that it will block until there is an item in
the queue to process and the current item is finished being processed?
Is this an application for some sort of ThreadPool construct?

The code below is an abstraction of a rough 1st attempt and included to
facilitate discussion. All comments, ideas and criticism are welcome.


Thanks,

Bill


//Project MyNamespace1.csproj
//Source: MyNamespace1.cs
//Creates: MyNamespace1.exe
using MyNamespace2

namespace MyNamespace1 {
public partial class MyService : ServiceBase
{
private MyWorkObject myWorkObject;

public MyService()
{
InitializeComponent();
myWorkObject = new MyWorkObject();
}

protected override void OnStart(string[] args)
{
myWorkObject.MyWorkerThreadRun = true;
}

protected override void OnStop()
{
myWorkObject.MyWorkerThreadRun = false;
}
}
}

//Project MyNamespace2.csproj
//Source: MyNamespace2.cs
//Creates: MyNamespace2.dll
namespace MyNamespace2 {
public class MyWorkObject {

private static MyWorkQueue1 myWorkQueue1;
private static MyWorkQueue2 myWorkQueue2;
private Thread myWorkerThread;

private bool myWorkerThreadRunning;
private bool myWorkerThreadRun;
public bool MyWorkerThreadRun {
set {
myWorkerThreadRun = value;
if (myWorkerThreadRun && !myWorkerThreadRunning)
myWorkerThread.Start();
}
}

public MyObject() {
myWorkerThread = new Thread(this.myWorker);
}

private void myWorker() {
myWorkQueue1 = new MyWorkQueue1();
myWorkQueue2 = new MyWorkQueue2();

try {
while (myWorkerThreadRun) {
if (!myWorkerThreadRunning) {
myWorkerThreadRunning = true;
}

myWorkQueue1.Process();
myWorkQueue2.Process();
Thread.Sleep(20);
}
} catch (Exception ex) {
//do some logging
} finally {
myWorkerThreadRunning = false;
}//end try
}//end myWorker
}//end MyWorkObject
}//end MyNamespace2
 
B

Bill McCormick

Peter said:
On Fri, 19 Dec 2008 14:11:45 -0800, Bill McCormick
[SNIP]
2. If garbage collection is no panacea, what questions do I need to
ask to determine the disposal process of MyObject and objects that it
creates?

I don't understand this question. Of course, you should not rely on GC
to deal with thread management, but then I didn't support that you
thought that you did. GC is fine for dealing with memory management,
and for other things you need to include logic to shut things down when
they are no longer needed.
Right. This has less to do with thread management and more to do with GC
under a Windows service. I should have chosen a better subject line.
Still, I suppose memory needs to managed correctly if the application is
to manage threads correctly.

Anyway ... I'm not sure if there should be any sort of object disposal
when the service is stopped. Or maybe I should be creating the process
objects OnStart and doing some cleanup/disposal OnStop?

Perhaps the right question to ask is this: when does the service
constructor run? Just before OnStart? Or does it run when the Windows
service manager loads the service? If it runs on service load, then it
would seem not to make sense to have a stopped service burden the OS
with unused recourses (memory)? And if so, then should object creation
generally happen OnStart? Which would require disposal OnStop? If,
instead, the constructor runs just before OnStart, then should some
cleanup OnStop be done for objects that won't get GC'd? Maybe a more
general question is: are there rules or extra considerations for GC in a
Windows service?
The ThreadPool could be used, yes. In that case, you'd probably keep
track of whether there's already a worker thread doing the processing,
queueing a new work item to the ThreadPool if there's not. The worker
thread would in turn keep working until it's exhausted the queue, and
then would exit.

I think it's more traditional though to just create a dedicated thread
for the purpose of processing the queue. In that case, you can use the
Monitor class to do things like Wait() and Pulse() so that consumers and
producers can communicate changes to the queue to each other.

For the latter, do you have a design pattern or some other sort of
example I could reference?


Thanks,

Bill
 
P

Peter Morris

I tend not to implement the service code within the service itself. I
create dedicated classes for handling different parts of the solution

A: Monitor directory for new files.
B: A factory to create a concrete descendant of FileProcessor depending on
the file name / location.
C: A queue to hold instances of Process in so that they can be processed in
order of receipt.
D: A dequeuer which has X number of worker threads which dequeues the next
job from the queue. I went for this approach because I had 3 queues, high,
normal, low priority.
E: The various FileProcessor descendants.

The classes above act as my "business domain" (what to do), leaving the sole
responsibility of the service to be to link them together and start/stop the
dequeuer (an application layer that coordinates the business domain
objects).

The monitor's NewFile event is wired up to an event that pushes the file
name into the process factory. The process factory returns the
FileProcessor (which has a Priority property) and I add that FileProcessor
to the relevant queue. The dequeuer is constantly running so it just
executes each process in turn. When I stop the service I tell the monitor
to stop looking for new files, I tell the dequeuer to empty its queues.
When I start the service I tell the monitor to start looking for files (on
startup it triggers the event for every file that already exists), and the
dequeuer to reactivate.

The service keeps requesting more time to stop while it waits for the
current processors to finish, this prevents you from restarting the service
before it has finished stopping.

It also makes it very easy to unit test :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top