268 lines
10 KiB
XML
268 lines
10 KiB
XML
<sect1>
|
|
<title>Pooling example</title>
|
|
<para>
|
|
The idea is to build an executor backed by a pool of
|
|
<literal>QueuedExecutor</literal>: 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
|
|
<literal>grep</literal>-like file scans). <emphasis>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 <ulink url="http://opensource.atlassian.com/confluence/spring/display/NET/Project+Structure">(instructions)</ulink> or from the download section of the
|
|
Spring.NET website that contains an .zip with the full CVS tree.</emphasis>
|
|
</para>
|
|
<para>
|
|
Some information on <literal>QueuedExecutor</literal> 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.
|
|
</para>
|
|
<para>
|
|
A <literal>QueuedExecutor</literal> is an executor where
|
|
<literal>IRunnable</literal> instances are run serialy by a worker
|
|
thread. When you <literal>Execute</literal> with a
|
|
<literal>QueuedExecutor</literal>, 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.
|
|
</para>
|
|
<para>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.
|
|
</para>
|
|
<para>
|
|
The example project <literal>Spring.Examples.Pool</literal> provides
|
|
an implementation of a pooled executor, backed by n instances of
|
|
<literal>Spring.Threading.QueuedExecutor</literal>: please ignore
|
|
the fact that <literal>Spring.Threading</literal> includes already a
|
|
very different implementation of a <literal>PooledExecutor</literal>:
|
|
here we wanto to use a pool of <literal>QueuedExecutor</literal>s.
|
|
</para>
|
|
<para>
|
|
This executor will be used to implement a parallel
|
|
recursive <literal>grep</literal>-like console executable.
|
|
</para>
|
|
<sect2>
|
|
<title>Implementing <literal>Spring.Pool.IPoolableObjectFactory</literal></title>
|
|
<para>
|
|
In order to use the <literal>SimplePool</literal> implementation,
|
|
the first thing to do is to implement the <literal>IPoolableObjectFactory</literal>
|
|
interface. This interface is intended to be implemented by objects
|
|
that can create the type of objects that should be pooled.
|
|
The <literal>SimplePool</literal>
|
|
will call the lifecycle methods on <literal>IPoolableObjectFactory</literal> interface
|
|
(<literal>MakeObject, ActivateObject, ValidateObject, PassivateObject, and DestroyObject</literal>)
|
|
as appropriate when the pool is created, objects are borrowed and returned to the pool, and when
|
|
the pool is destroyed.
|
|
</para>
|
|
<para>
|
|
In our case, as already said, we want to to implement a pool
|
|
of <literal>QueuedExecutor</literal>. Ok, here the declaration:
|
|
<programlisting language="csharp">public class QueuedExecutorPoolableFactory : IPoolableObjectFactory
|
|
{</programlisting>
|
|
the first task a factory should do is to create objects:
|
|
<programlisting language="csharp">object IPoolableObjectFactory.MakeObject()
|
|
{
|
|
// to actually make this work as a pooled executor
|
|
// use a bounded queue of capacity 1.
|
|
// If we don't do this one of the queued executors
|
|
// will accept all the queued IRunnables as, by default
|
|
// its queue is unbounded, and the PooledExecutor
|
|
// will happen to always run only one thread ...
|
|
return new QueuedExecutor(new BoundedBuffer(1));
|
|
}</programlisting>
|
|
and should be also able to destroy them:
|
|
<programlisting language="csharp">void IPoolableObjectFactory.DestroyObject(object o)
|
|
{
|
|
// ah, self documenting code:
|
|
// Here you can see that we decided to let the
|
|
// executor process all the currently queued tasks.
|
|
QueuedExecutor executor = o as QueuedExecutor;
|
|
executor.ShutdownAfterProcessingCurrentlyQueuedTasks();
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
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:
|
|
<programlisting language="csharp">void IPoolableObjectFactory.ActivateObject(object o)
|
|
{
|
|
QueuedExecutor executor = o as QueuedExecutor;
|
|
executor.Restart();
|
|
}</programlisting>
|
|
even if a <literal>QueuedExecutor</literal> restarts itself as
|
|
needed and so a valid implementation could leave this method empty.
|
|
</para>
|
|
<para>
|
|
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
|
|
<footnote>
|
|
<para>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
|
|
</para>
|
|
</footnote>).
|
|
Here we check that the worker thread exists:
|
|
<programlisting language="csharp">bool IPoolableObjectFactory.ValidateObject(object o)
|
|
{
|
|
QueuedExecutor executor = o as QueuedExecutor;
|
|
return executor.Thread != null;
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
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:
|
|
<programlisting language="csharp">void IPoolableObjectFactory.PassivateObject(object o)
|
|
{
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
At this point, creating a pool is simply a matter of creating an
|
|
<literal>SimplePool</literal> as in:
|
|
<programlisting language="csharp">pool = new SimplePool(new QueuedExecutorPoolableFactory(), size);</programlisting>
|
|
</para>
|
|
</sect2>
|
|
<sect2>
|
|
<title>Being smart using pooled objects</title>
|
|
<para>
|
|
Taking advantage of the <literal>using</literal> keyword seems
|
|
to be very important in these <literal>c#</literal> days, so we
|
|
implement a very simple helper (<literal>PooledObjectHolder</literal>)
|
|
that can allow us to do things like:
|
|
<programlisting language="csharp">using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool))
|
|
{
|
|
QueuedExecutor executor = (QueuedExecutor) holder.Pooled;
|
|
executor.Execute(runnable);
|
|
}</programlisting>
|
|
without worrying about obtaining and returning an object from/to the
|
|
pool.
|
|
</para>
|
|
<para>
|
|
Here is the implementation:
|
|
<programlisting language="csharp">public class PooledObjectHolder : IDisposable
|
|
{
|
|
IObjectPool pool;
|
|
object pooled;
|
|
|
|
/// <summary>
|
|
/// Builds a new <see cref="PooledObjectHolder"/>
|
|
/// trying to borrow an object form it
|
|
/// </summary>
|
|
/// <param name="pool"></param>
|
|
private PooledObjectHolder(IObjectPool pool)
|
|
{
|
|
this.pool = pool;
|
|
this.pooled = pool.BorrowObject();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allow to access the borrowed pooled object
|
|
/// </summary>
|
|
public object Pooled
|
|
{
|
|
get
|
|
{
|
|
return pooled;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the borrowed object to the pool
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
pool.ReturnObject(pooled);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new <see cref="PooledObjectHolder"/> for the
|
|
/// given pool.
|
|
/// </summary>
|
|
public static PooledObjectHolder UseFrom(IObjectPool pool)
|
|
{
|
|
return new PooledObjectHolder(pool);
|
|
}
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
Please don't forget to destroy all the pooled istances once you have
|
|
finished! How? Well using something like this in
|
|
<literal>PooledQueuedExecutor</literal>:
|
|
<programlisting language="csharp">public void Stop ()
|
|
{
|
|
// waits for all the grep-task to have been queued ...
|
|
foreach (ISync sync in syncs)
|
|
{
|
|
sync.Acquire();
|
|
}
|
|
pool.Close();
|
|
}</programlisting>
|
|
</para>
|
|
</sect2>
|
|
<sect2>
|
|
<title>Using the executor to do a parallel <literal>grep</literal></title>
|
|
<para>
|
|
The use of the just built executor is quite straigtforward but a
|
|
little tricky if we want to really exploit the pool.
|
|
<programlisting language="csharp">private PooledQueuedExecutor executor;
|
|
|
|
public ParallelGrep(int size)
|
|
{
|
|
executor = new PooledQueuedExecutor(size);
|
|
}
|
|
|
|
public void Recurse(string startPath, string filePattern, string regexPattern)
|
|
{
|
|
foreach (string file in Directory.GetFiles(startPath, filePattern))
|
|
{
|
|
executor.Execute(new Grep(file, regexPattern));
|
|
}
|
|
foreach (string directory in Directory.GetDirectories(startPath))
|
|
{
|
|
Recurse(directory, filePattern, regexPattern);
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
executor.Stop();
|
|
}</programlisting>
|
|
</para>
|
|
<para>
|
|
<programlisting language="csharp">public static void Main(string[] args)
|
|
{
|
|
if (args.Length < 3)
|
|
{
|
|
Console.Out.WriteLine("usage: {0} regex directory file-pattern [pool-size]", Assembly.GetEntryAssembly().CodeBase);
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
string regexPattern = args[0];
|
|
string startPath = args[1];
|
|
string filePattern = args[2];
|
|
int size = 10;
|
|
try
|
|
{
|
|
size = Int32.Parse(args[3]);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
Console.Out.WriteLine ("pool size {0}", size);
|
|
|
|
ParallelGrep grep = new ParallelGrep(size);
|
|
grep.Recurse(startPath, filePattern, regexPattern);
|
|
grep.Stop();
|
|
}</programlisting>
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
|