编辑7.1. 简介
Spring.Threading命名空间的目的,是用一系列功能强大的抽象来增强FCL中的相关功能。由于Doug Lea在他的java版EDU.oswefo.cs.dl.util.concurrent类库中已经定义了很多用于并发操作的成熟类型,所以我们决定将其中的一部分导入到Spring.NET中来。到目前为止我们只导入了3个类,包括用来支持AOP池化方面(AOP based pooling aspect)、提供对象池基本功能的类和Semaphore类(.NET 1.0/1.1没有相应的类型,2.0中新增了Semaphore类)。
另外,在Java 5中,java.util.concurrent包中的类也建立在Doug Lea的类库之上,并且已经通过了严格的审查。Java 5类库及.NET 2.0中新增的类型有可能导致Spring在这方面进行修改。
Spring.Threading中还有一个很重要的工具类LogicalThreadContext,用于进行线程的本地存储。
顶部
编辑7.2. 线程本地存储
根据运行环境的不同,在线程本地存储中保存对象也需要应用不同的策略。在Web应用程序中,如果一个请求可能会被多个线程处理,线程局部对象就应该保存在HttpContext.Current中。其它情况则可使用 System.Runtime.Remoting.Messaging.CallContext。这两种策略的选择依据,以及与 [ThreadStatic]特性的比较可以参考“Piers7”的blog和Spring.NET的论坛。而LogicalThreadContext则把这方面的细节隐藏了起来,所以可以使代码更具可移植性。
LogicalThreadContext类的定义相当简单:
public sealed class LogicalThreadContext
{
public static object GetData(string name)
public static void SetData(string name, object value)
public static void FreeNamedDataSlot(string name)
}
该类定义了一系列静态方法,可以用字符串作为键值设置或读取数据。如果想要释放存储区的空间,可以调用FreeNamedDataSlot方法。
顶部编辑7.3. 同步基础
用户可能会很奇怪,既然System.Threading已经提供了那么多用于同步的类,为什么Spring.NET还要定义这些同步类型呢?这是因为System.Threading中的相关类型虽多,却没有为我们提供一个优雅的抽象或接口,我们只能在一个很低的层面编写代码。凭经验来讲,要让代码工作的好,我们最终还是得拿出一个抽象层次来。Doug Lea已经在这方面作了很多研究,我们可以充分的利用他的研究成果。
顶部
编辑7.3.1. ISync
ISync是所有用于控制多线程资源访问的类的核心接口。这个接口很简单,主要有两种基本的用途。第一种是用来阻塞当前线程,直到满足了某个条件:
void ConcurrentRun(ISync lock) {
lock.Acquire(); // block until condition met
try {
// ... access shared resources
}
finally {
lock.Release();
}
}
另一种是用来指定在满足某个条件前将线程阻塞的最多次数:
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
}
}
顶部
编辑7.3.2. SyncHolder
SyncHolder类实现了System.IDisposable接口,利用这个类我们可以在c#的using语句中使用ISync接口:进入 using语句后ISync的Acquire方法会自动调用,退出using语句后ISync的Release方法会被自动调用。
使用using语句可以简化编程模型,请看下面的例子:
ISync sync = ...
...
using (new SyncHolder(sync))
{
// ... code to be executed
// holding the ISync lock
}
当然这是同步版本(按:timed version), 如果要自己处理超时,就稍微麻烦一点儿:
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
}
编辑7.3.3. Latch
Latch类实现了ISync接口,它是一个闭锁。闭锁是一个布尔条件,最多只能设置一次。一旦闭锁开放,所有等待着的线程都会解除阻塞。这就如同一个初始状态设置为非终止的ManualResetEvent对象(即创建时传给构造器的参数值为false,或者调用Reset将事件状态设置为非终止状态,从而导致其它线程阻塞)一样,只有在调用了Set()方法将事件状态设为终止之后,才能允许其它等待着的线程继续执行。Latch典型的用法是作为一组工作线程的开始信号。
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();
}
}
顶部
编辑7.3.4.信号量
Semaphore类实现了ISync接口,实现了信号量的功能。从概念上说,信号量维护了一组许可证,用来控制允许同时进入同步代码的线程数。每次Acquire方法调用都需要等待有一个许可证变为可用,然后便占用此许可证。每次调用Release方法后都会释放一个许可证。其实 Semaphore并没有使用实际的“许可证”对象,只是在内部维护了一个许可证的可用数量。最典型的用法是控制对共享对象池的访问。
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();
}
}
}
顶部