Integrate a Queue with an ObjectPool

C

Craig Buchanan

I would like to have a limited pool of objects (the objects are expensive to
create) that can be enlisted to process items in a queue. Moreover, I
would like to be able to have the processing (time-consuming) occur on a
background thread.

I have two questions:
* what is a good way to block the queue loop while it waits for a free
worker?
* is my approach a reasonable one?

Thanks,

Craig Buchanan

Here's my psedo-code:

Sub ProcessQueue

Do While Queue.Count > 0

'wait for next available worker
Worker = ObjectPool.getFreeWorker()

'assign next item in the queue to the worker
Worker.Item = Queue.Dequeue

'run time-consuming process in background
ThreadPool.QueueUserWorkItem(AddressOf ProcessItem, Worker)

Loop

End Sub

Sub ProcessItem(State As Object)

Dim WorkerAs Worker= CType(State,Worker)

Try

'process item
Worker.Process()

Catch ex As Exception

'return item to queue
Queue.Enqueue(Worker.Item)

Finally

'return worker to pool
ObjectPool.returnWorker(Worker)

End Try

End Sub
 
C

Chris Mullins [MVP]

I've written this code more times than I can count, so I hope it's a decent
pattern to follow.

One of these days soon, I'll post my ObjectPool<T> classes to my blog, along
with some instructions on how to use them. This problem seems to come up
very frequently when writing server applications - we've got dozens of
"expensive" object pools that we maintain. Everything from AD & LDAP
connections, to pinned byte[] buffers for use in reading & writing to
Sockets (to avoid heap fragmentation).

It's got a nice robust mechanism for checking objects in & out of the pool
(using IDisposable so that 'using' blocks can be employed, and also using
Object Resurrection so that the Finalizer is run, the expensive object
doesn't get lost) and its very easy to use.

You wait problem, as you've described it, I usually see it solved with the
Monitor.Pulse / PulseAll pattern. Worker threads are kept blocked in a
Monitor, and when data is added to the queue, the Monitor is Pulsed
realeasing one of the waiting threads. You've got to be carefull of race
conditions, but it's a very well worn pattern.
 
C

Craig Buchanan

Chris-

Thanks for the reply. Does your approach also use the ThreadPool to process
the long-running job?

Any chance I could see the code? craig dot buchanan at cogniza dot com. is
this your blog: http://instructors.cwrl.utexas.edu/jesson/?q=blog/61 ?

Thanks,

Craig

Chris Mullins said:
I've written this code more times than I can count, so I hope it's a
decent pattern to follow.

One of these days soon, I'll post my ObjectPool<T> classes to my blog,
along with some instructions on how to use them. This problem seems to
come up very frequently when writing server applications - we've got
dozens of "expensive" object pools that we maintain. Everything from AD &
LDAP connections, to pinned byte[] buffers for use in reading & writing to
Sockets (to avoid heap fragmentation).

It's got a nice robust mechanism for checking objects in & out of the pool
(using IDisposable so that 'using' blocks can be employed, and also using
Object Resurrection so that the Finalizer is run, the expensive object
doesn't get lost) and its very easy to use.

You wait problem, as you've described it, I usually see it solved with the
Monitor.Pulse / PulseAll pattern. Worker threads are kept blocked in a
Monitor, and when data is added to the queue, the Monitor is Pulsed
realeasing one of the waiting threads. You've got to be carefull of race
conditions, but it's a very well worn pattern.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise, Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

Craig Buchanan said:
I would like to have a limited pool of objects (the objects are expensive
to create) that can be enlisted to process items in a queue. Moreover, I
would like to be able to have the processing (time-consuming) occur on a
background thread.

I have two questions:
* what is a good way to block the queue loop while it waits for a free
worker?
* is my approach a reasonable one?

Thanks,

Craig Buchanan

Here's my psedo-code:

Sub ProcessQueue

Do While Queue.Count > 0

'wait for next available worker
Worker = ObjectPool.getFreeWorker()

'assign next item in the queue to the worker
Worker.Item = Queue.Dequeue

'run time-consuming process in background
ThreadPool.QueueUserWorkItem(AddressOf ProcessItem, Worker)

Loop

End Sub

Sub ProcessItem(State As Object)

Dim WorkerAs Worker= CType(State,Worker)

Try

'process item
Worker.Process()

Catch ex As Exception

'return item to queue
Queue.Enqueue(Worker.Item)

Finally

'return worker to pool
ObjectPool.returnWorker(Worker)

End Try

End Sub
 
C

Craig Buchanan

Chris-

I'm wondering if it would make sense for the ObjectPool to drive the process
of 'draining' the queue. Think of how bank tellers process a line of
customers.

Perhaps there would be an event (or delegate) that would fire when an item
in the pool becomes free. in the event's code, the worker would grab the
next time in the queue.

Seems like there would need to be a method on the ObjectPool to signal this
process to begin. Perhaps it could listen to an event raised by
queue.enqueue().

Thoughts?

Chris Mullins said:
I've written this code more times than I can count, so I hope it's a
decent pattern to follow.

One of these days soon, I'll post my ObjectPool<T> classes to my blog,
along with some instructions on how to use them. This problem seems to
come up very frequently when writing server applications - we've got
dozens of "expensive" object pools that we maintain. Everything from AD &
LDAP connections, to pinned byte[] buffers for use in reading & writing to
Sockets (to avoid heap fragmentation).

It's got a nice robust mechanism for checking objects in & out of the pool
(using IDisposable so that 'using' blocks can be employed, and also using
Object Resurrection so that the Finalizer is run, the expensive object
doesn't get lost) and its very easy to use.

You wait problem, as you've described it, I usually see it solved with the
Monitor.Pulse / PulseAll pattern. Worker threads are kept blocked in a
Monitor, and when data is added to the queue, the Monitor is Pulsed
realeasing one of the waiting threads. You've got to be carefull of race
conditions, but it's a very well worn pattern.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise, Microsoft C# MVP
http://www.coversant.com/blogs/cmullins

Craig Buchanan said:
I would like to have a limited pool of objects (the objects are expensive
to create) that can be enlisted to process items in a queue. Moreover, I
would like to be able to have the processing (time-consuming) occur on a
background thread.

I have two questions:
* what is a good way to block the queue loop while it waits for a free
worker?
* is my approach a reasonable one?

Thanks,

Craig Buchanan

Here's my psedo-code:

Sub ProcessQueue

Do While Queue.Count > 0

'wait for next available worker
Worker = ObjectPool.getFreeWorker()

'assign next item in the queue to the worker
Worker.Item = Queue.Dequeue

'run time-consuming process in background
ThreadPool.QueueUserWorkItem(AddressOf ProcessItem, Worker)

Loop

End Sub

Sub ProcessItem(State As Object)

Dim WorkerAs Worker= CType(State,Worker)

Try

'process item
Worker.Process()

Catch ex As Exception

'return item to queue
Queue.Enqueue(Worker.Item)

Finally

'return worker to pool
ObjectPool.returnWorker(Worker)

End Try

End Sub
 
B

Brian Gideon

I would like to have a limited pool of objects (the objects are expensive to
create) that can be enlisted to process items in a queue. Moreover, I
would like to be able to have the processing (time-consuming) occur on a
background thread.

I have two questions:
* what is a good way to block the queue loop while it waits for a free
worker?

The best way to block the loop is to block the getFreeWorker method
until a free worker is available. The pattern you're looking for is a
producer-consumer queue or blocking queue. Look for the
ProducerConsumer class in the following article. The Produce and
Consume methods in that class would map directly to your returnWorker
and getFreeWorker methods respectively. I know the examples in the
article are in C#, but you'll get the idea.

http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml
* is my approach a reasonable one?

Yes, I think it is reasonable. There might be some minor changes I
would change regarding your general pattern, but nothing worth
mentioning.
 
C

Craig Buchanan

Thanks for the help.

Craig

Chris Mullins said:
Nope, not my blog. I'll give ya a hint though: look at the bottom of this
message. The owner of that blog would probably cry if he was forced to
ready my poor writing all day long...

I'll post the ObjectPool<T> code to my blog in a few days (when I write my
next entry).

Ping me if I forget...
 

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