Files
spring-net/doc/reference/src/threading.xml
sbohlen 9788fd3580 SPRNET-1407
Updated copyright notices in docs.
2010-12-11 20:35:49 +00:00

264 lines
9.8 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<chapter xml:id="threading" xmlns="http://docbook.org/ns/docbook" version="5">
<title>Threading and Concurrency Support</title>
<sect1 xml:id="threading-introduction">
<title>Introduction</title>
<para>The purpose of the <literal>Spring.Threading</literal> namespace
is to provide a place to keep useful concurrency abstractions that augment
those in the BCL. Since Doug Lea has provided a wealth of mature public
domain concurrency abstractions in his Java based
'EDU.oswego.cs.dl.util.concurrent' libraries we decided to port a few of
his abstractions to .NET. So far, we've only ported three classes, the
minimum necessary to provide basic object pooling functionality to support
an AOP based pooling aspect and to provide a Semaphore class that was
mistakenly not included in .NET 1.0/1.1.</para>
<para>There is also an important abstraction, IThreadStorage, for
performing thread local storage.</para>
</sect1>
<sect1>
<title>Thread Local Storage</title>
<para>Depending on your runtime environment there are different strategies
to use for storing objects in thread local storage. If you are in web
applications a single Request may be executed on different threads. As
such, the location to store thread local objects is in
<literal>HttpContext.Current</literal>. For other environments
<literal>System.Runtime.Remoting.Messaging.CallContext</literal> is
used. For more background information on the motivation behind these
choices, say as compared to the attribute [ThreadStatic] refer to
"Piers7"'s <ulink
url="http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html">blog</ulink>
and this <ulink
url="http://forum.springframework.net/showthread.php?t=572&amp;highlight=LogicalThreadContext">forum
post</ulink>. The interface IThreadStorage serves as the basis for the
thread local storage abstraction and various implementations can be
selected from depending on your runtime requirements. Configuring the
implementation of IThreadStorage makes it easier to have more portability
across runtime environments.</para>
<para>The API is quite simple and shown below<programlisting language="csharp">public interface IThreadStorage
{
object GetData(string name)
void SetData(string name, object value)
void FreeNamedDataSlot(string name)
}
</programlisting></para>
<para>The methods <methodname>GetData</methodname> and
<methodname>SetData</methodname> are responsible for retrieving and
setting the object that is to be bound to thread local storage and
associating it with a name. Clearing the thread local storage is done via
the method <methodname>FreeNamedDataSlot</methodname>.</para>
<para>In <literal>Spring.Core</literal> is the implementation,
<literal>CallContextStorage</literal>, that directly uses
<literal>CallContext</literal> and also the implementation
<literal>LogicalThreadContext</literal> which by default uses
<literal>CallContextStorage</literal> but can be configured via the
static method <methodname>SetStorage(IThreadStorage)</methodname>. The
methods on CallContextStorage and LogicalThreadContext are static.</para>
<para>In <literal>Spring.Web</literal> is the implementation
<literal>HttpContextStorage</literal> which uses the
<literal>HttpContext</literal> to store thread local data and
<literal>HybridContextStorage</literal> that uses
<literal>HttpContext</literal> if within a web environment, i.e.
<literal>HttpContext.Current != null</literal>, and
<literal>CallContext</literal> otherwise.</para>
<para>Spring internally uses <literal>LogicalThreadContext</literal>
as this doesn't require a coupling to the <package>System.Web</package>
namespace. In the case of Spring based web applications, Spring's
<literal>WebSupportModule</literal> sets the storage strategy of
<literal>LogicalThreadContext</literal> to be
<literal>HybridContextStorage</literal>.</para>
</sect1>
<sect1>
<title>Synchronization Primitives</title>
<para>When you take a look at these synchronization classes, you'll wonder
why it's even necessary when <literal>System.Threading</literal> provides
plenty of synchronization options. Although
<literal>System.Threading</literal> provides great synchronization
classes, it doesn't provide well-factored abstractions and interfaces for
us. Without these abstractions, we will tend to code at a low-level. With
enough experience, you'll eventually come up with some abstractions that
work well. Doug Lea has already done a lot of that research and has a
class library that we can take advantage of.</para>
<sect2>
<title>ISync</title>
<para><literal>ISync</literal> is the central interface for all classes
that control access to resources from multiple threads. It's a simple
interface which has two basic use cases. The first case is to block
indefinitely until a condition is met:</para>
<programlisting language="csharp">void ConcurrentRun(ISync lock) {
lock.Acquire(); // block until condition met
try {
// ... access shared resources
}
finally {
lock.Release();
}
}
</programlisting>
<para>The other case is to specify a maximum amount of time to block
before the condition is met:</para>
<programlisting language="csharp">void ImpatientConcurrentRun(ISync lock) {
// block for at most 10 milliseconds for condition
if ( lock.Attempt(10) ) {
try {
// ... access shared resources
}
finally {
lock.Release();
}
} else {
// complain of time out
}
}
</programlisting>
</sect2>
<sect2>
<title>SyncHolder</title>
<para>The <literal>SyncHolder</literal> class implements the
<literal>System.IDisposable</literal> interface and so provides a way to
use an <literal>ISync</literal> with the <literal>using</literal> C#
keyword: the <literal>ISync</literal> will be automatically
<literal>Acquire</literal>d and then <literal>Release</literal>d on
exiting from the block.</para>
<para>This should simplify the programming model for code using (!) an
<literal>ISync</literal>: <programlisting language="csharp">
ISync sync = ...
...
using (new SyncHolder(sync))
{
// ... code to be executed
// holding the ISync lock
}
</programlisting> There is also the timed version, a little more
cumbersome as you must deal with timeouts: <programlisting language="csharp">
ISync sync = ...
long msecs = 100;
...
// try to acquire the ISync for msecs milliseconds
try
{
using (new SyncHolder(sync, msecs))
{
// ... code to be executed
// holding the ISync lock
}
}
catch (TimeoutException)
{
// deal with failed lock acquisition
}
</programlisting></para>
</sect2>
<sect2>
<title>Latch</title>
<para>The <literal>Latch</literal> class implements the
<literal>ISync</literal> interface and provides an implementation of a
<emphasis>latch</emphasis>. A latch is a boolean condition that is set
at most once, ever. Once a single release is issued, all acquires will
pass. It is similar to a <literal>ManualResetEvent</literal> initialized
unsignalled (Reset) and can only be <literal>Set()</literal>. A typical
use is to act as a start signal for a group of worker threads.</para>
<programlisting language="csharp">class Boss {
Latch _startPermit;
void Worker() {
// very slow worker initialization ...
// ... attach to messaging system
// ... connect to database
_startPermit.Acquire();
// ... use resources initialized in Mush
// ... do real work
}
void Mush() {
_startPermit = new Latch();
for (int i=0; i&lt;10; ++i) {
new Thread(new ThreadStart(Worker)).Start();
}
// very slow main initialization ...
// ... parse configuration
// ... initialize other resources used by workers
_startPermit.Release();
}
}</programlisting>
</sect2>
<sect2>
<title>Semaphore</title>
<para>The <literal>Semaphore</literal> class implements the
<literal>ISync</literal> interface and provides an implementation of a
semaphore. Conceptually, a semaphore maintains a set of permits. Each
<literal>Acquire()</literal> blocks if necessary until a permit is
available, and then takes it. Each <literal>Release()</literal> adds a
permit. However, no actual permit objects are used; the Semaphore just
keeps a count of the number available and acts accordingly. A typical
use is to control access to a pool of shared objects.</para>
<programlisting language="csharp">class LimitedConcurrentUploader {
// ensure we don't exceed maxUpload simultaneous uploads
Semaphore _available;
public LimitedConcurrentUploader(maxUploads) {
_available = new Semaphore(maxUploads);
}
// no matter how many threads call this method no more
// than maxUploads concurrent uploads will occur.
public Upload(IDataTransfer upload) {
_available.Acquire();
try {
upload.TransferData();
}
finally {
_available.Release();
}
}
}
</programlisting>
</sect2>
</sect1>
</chapter>