%@ CodeTemplate %>
<%@ Import Namespace="System.IO" %>
<%@ Assembly Name="AgileDocs.Core" %>
<%@ Import Namespace="AgileDocs.Core" %>
Pooling example
The idea is to build an executor backed by a pool of
QueuedExecutor: this will show how Spring.NET
provides some useful low-level/high-quality reusable threading and
pooling abstractions.
This executor will provide parallel executions (in our case
grep-like file scans). Note: This example
is not in the 1.0.0 release to its use of classes in the Spring.Threading
namespace scheduled for release in Spring 1.1. To access ths example
please get the code from CVS (instructions) or from the download section of the
Spring.NET website that contains an .zip with the full CVS tree.
Some information on QueuedExecutor is helpful to
better understand the implementation and to possibly disagree with it.
Keep in mind that the point is to show how to develop your own
object-pool.
A QueuedExecutor is an executor where
IRunnable instances are run serialy by a worker
thread. When you Execute with a
QueuedExecutor, your request is queued; at some
point in the future your request will be taken and executed by the
worker thread: in case of error the thread is terminated.
However
this executor recreates its worker thread as needed.
Last but not least, this executor can be shut down in
a few different ways (please refer to the Spring.NET SDK documentation).
Given its simplicity, it is very powerful.
The example project Spring.Examples.Pool provides
an implementation of a pooled executor, backed by n instances of
Spring.Threading.QueuedExecutor: please ignore
the fact that Spring.Threading includes already a
very different implementation of a PooledExecutor:
here we wanto to use a pool of QueuedExecutors.
This executor will be used to implement a parallel
recursive grep-like console executable.
Implementing Spring.Pool.IPoolableObjectFactory
In order to use the SimplePool implementation,
the first thing to do is to implement the IPoolableObjectFactory
interface. This interface is intended to be implemented by objects
that can create the type of objects that should be pooled.
The SimplePool
will call the lifecycle methods on IPoolableObjectFactory interface
(MakeObject, ActivateObject, ValidateObject, PassivateObject, and DestroyObject)
as appropriate when the pool is created, objects are borrowed and returned to the pool, and when
the pool is destroyed.
In our case, as already said, we want to to implement a pool
of QueuedExecutor. Ok, here the declaration:
<%= PoolExample("PooledQueuedExecutor.cs", "factory-declaration") %>
the first task a factory should do is to create objects:
<%= PoolExample("PooledQueuedExecutor.cs", "make") %>
and should be also able to destroy them:
<%= PoolExample("PooledQueuedExecutor.cs", "destroy") %>
When an object is taken from the pool, to satisfy a client request,
may be the object should be activated. We can possibly implement the
activation like this:
<%= PoolExample("PooledQueuedExecutor.cs", "activate") %>
even if a QueuedExecutor restarts itself as
needed and so a valid implementation could leave this method empty.
After activation, and before the pooled object can be succesfully
returned to the client, it is validated (should the object be
invalid, it will be discarded: this can lead to an empty unusable
pool
You may think that we can provide a smarter
implementation and you are probably right. However, it is not so
difficult to create a new pool in case the old one became unusable.
It could not be your preferred choice but surely it leverages
simplicity and object immutability
).
Here we check that the worker thread exists:
<%= PoolExample("PooledQueuedExecutor.cs", "validate") %>
Passivation, symmetrical to activation, is the process a pooled
object is subject to when the object is returned to the pool. In our
case we simply do nothing:
<%= PoolExample("PooledQueuedExecutor.cs", "passivate") %>
At this point, creating a pool is simply a matter of creating an
SimplePool as in:
<%= PoolExample("PooledQueuedExecutor.cs", "create-pool") %>
Being smart using pooled objects
Taking advantage of the using keyword seems
to be very important in these c# days, so we
implement a very simple helper (PooledObjectHolder)
that can allow us to do things like:
<%= PoolExample("PooledQueuedExecutor.cs", "execute") %>
without worrying about obtaining and returning an object from/to the
pool.
Here is the implementation:
<%= PoolExample("PooledQueuedExecutor.cs", "holder") %>
Please don't forget to destroy all the pooled istances once you have
finished! How? Well using something like this in
PooledQueuedExecutor:
<%= PoolExample("PooledQueuedExecutor.cs", "stop") %>
Using the executor to do a parallel grep
The use of the just built executor is quite straigtforward but a
little tricky if we want to really exploit the pool.
<%= PoolExample("Grep.cs", "parallel-grep-class") %>
<%= PoolExample("Grep.cs", "parallel-grep-main") %>