264 lines
9.8 KiB
XML
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&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<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> |