Sprin.NET连载2

来源:互联网 发布:手机淘宝好评在哪里找 编辑:程序博客网 时间:2024/04/30 14:53
 

16.4 ApplicationContextIEventRegistry

16.4.1 介绍

例程Spring.Examples.EventRegistry演示了怎样使用应用程序上下文艺松耦合的模式包装.NET事件。

松耦合模式通常合MOM(信息向导中间件)相连系,在那里一个后台进程充当与其它独立的进程通信的信息使者。进程间通过信息使者发送信息达到间接的通信目的。发起通信的进程是一个公开的发布者并且接受信息的进程是已知的订阅者。通过使用对中间件特定的API,这些进程给自己注册,即作为信息使者的发布者或者订阅者。发布者和订阅者之间的通信被认为是松耦合的,因为发布者或者订阅者之间都没有相互的直接引用。信息使者扮演了两个进程间的传送介质。IEventRegistry在应用到.NET事件时就扮演了和信息使者类似的角色。发布者调用.NET事件的类,订阅者在事件中注册的影响。并且在他们之间发送的信息是SystemEventArgs的实例。IeventRegistry的执行决定了通知类型的精确语义以及发布者与接受者之间的连接。

IapplicationContext接口扩展了IeventRegistry接口并且IapplicationContext的执行把时间注册功能委派给了Spring.Objects.Events.Support.EventRegistry对象的实例。IeventRegistry是一个具有一个公开签名和2个预定方法的简单接口。Section 3.14.4, “Loosely coupled events”被称做他们的签名的暗示。Spring.Objects.Events.Support.EventRegistry实现本质上是发布者和订阅者之间事件包装的一个便利。在这个实现里,事件包装完以后就完成了,发布者直接通过标准的.NET事件机制与订阅者联系。通过时间注册订阅给事件和接下来可靠的通知订阅者交替执行能更进一步的退耦。

在这个例子中,MyClientEventArgs类是System.EventArgs(定义了一个EventMessage字符串属性)的子类。MyEventPublisher定义了一个具有代理签名void SimpleClientEvent( object sender, MyClientEventArgs args ) 的公开事件。方法void ClientMethodThatTriggersEvent1()激发这个事件。在订阅一边,类MyEventSubscriber包含了一个方法HandleClientEvents(匹配代理的签名并且有一个Bool属性(如果方法被调用就设为true。))。

发布者和订阅者类被定义在应用程序上下文配置文件中,但是为了参与事件注册这并不是必须的。主程序EventRegistryApp创建应用程序上下文并且生成一个MyEventPublisher的实例。发布者通过调用随着时间注册而注册,ctx.PublishEvents( publisher )。事件注册保持了这个发布者的一个引用,是为了以后用来注册任何匹配它的事件签名的订阅者。接下来创建了两个订阅者,一个通过调用方法ctx.Subscribe( subscriber, typeof(MyEventPublisher) )被与发布者捆绑在一起,指定类型指示出订阅者应该只从MyEventPublisher类型被注册到事件。这扮演了一个在订阅者上的简单的过滤器机制。

发布者接下来使用正常的.NET事件语义来触发事件,并且订阅者被调用,订阅者在控制台打印出信息,并且设置一个转台变量显示已经被调用。程序间下来简单的打印两个订阅者的状态变量,显示它们中只有一个被调用了(就是那个随事件注册一起注册的)。

16.5池的实例

构建一个由QueuedExecutor的池的执行者的主意是:演示Spring.NET怎样提供一些低级/高质量可重复使用的线程和池提取。执行者将提供并行的操作(我们的例子是相似文件扫描)。注意:这个例子不在1.0.0 版本中而是在1.1 版本的Spring.Threading命名空间中。为了访问这个例子,请从CVS或者下载Spring.NET的站点(在CVS树立包含an.zip)获得代码.

QueuedExecutor中的一些信息是对于更好的理解执行和不能赞同都有用的。记住关键点来开发你自己的对象池。

一个QueuedExecutor就是一个执行者,在这里Irunnable实例通过一个工作者线程来达到不间断的运行。当你执行一个QueuedExecutor时,你的请求被加入到队列。在某种意义上,将来你的请求就会被工作者线程发现并执行,除非行程发生错误而被终止。但是执行者会重新创建需求的工作者线程。

最后至少,执行者可以用几种不同的方法关闭(请参考Spring.NET SDK文档)。尽管很简单,但它很强大。

例子工程Spring.Examples.Pool提供了一个执行者池的实现,由nSpring.Threading.QueuedExecutor实例支持:请忽略Spring.Threading已经包含了一个相当不同的PooledExecutor实现方式的事实:在这里我们想要使用一个QueuedExecutors池。

16.5.1 实现Spring.Pool.IPoolableObjectFactory

为了使用SimplePool,要做的第一件事就是实现IpoolableObjectFactory接口。这个接口故意用object来实现,这样就能创建object类型存到池里。SimplePool将当池被建立后SimplePool将在适当的时候调用IpoolableObjectFactory接口的生存周期方法,对象被借出和返回池,当池被销毁的时候。

在我们的程序中,就像上面说的,我们想要实现一个QueuedExecutor池。OK,在这里声名:

public class QueuedExecutorPoolableFactory : IPoolableObjectFactory

{

工厂要做的第一个任务实创建对象:

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));

}

并且应该能够销毁他们:

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();

}

当一个对象从池中拿出来,为了满足一个客户端的需求,可能对象应该很灵活。我们可能需要象这样的灵活:

void IPoolableObjectFactory.ActivateObject(object o)

{

    QueuedExecutor executor = o as QueuedExecutor;

    executor.Restart();

}

即使QueuedExecutor在需要的时候重新开始自己,并且这样一种有效的实现能委托方法为空。

在激活之后,并且池对象能成功地从客户端返回之前,对象应该被确认(对象应该无效,它将被丢弃:这样可能导致一个空的不能使用的池)。在这里我们检查工作者线程存在:

bool IPoolableObjectFactory.ValidateObject(object o)

{

    QueuedExecutor executor = o as QueuedExecutor;

    return executor.Thread != null;

}

钝化,活化的对应,就是那个使池对象在对象返回池时服从分配的进程。在我们的例子中,我们简化不做任何事:

void IPoolableObjectFactory.PassivateObject(object o)

{

}

在这点,创建一个池是创建一个SimplePool的简单事情,就像:

pool = new SimplePool(new QueuedExecutorPoolableFactory(), size);

16.5.2 巧妙的使用池对象

从分利用关键字好像在C#时代很重要,所以我们实现一个非常简单的助手(PlooledObjectHolder),它能允许我们做下面这样的事情:

using (PooledObjectHolder holder = PooledObjectHolder.UseFrom(pool))

{

    QueuedExecutor executor = (QueuedExecutor) holder.Pooled;

    executor.Execute(runnable);

}

不需要担心从池中获得和返回对象。这里是它的实现:

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);

    }

}

请不要忘记在你完成之后销毁所有的池实例。怎么做? 使用类似如下的PlloedQueuedExecutor:

public void Stop ()

{

    // waits for all the grep-task to have been queued ...

    foreach (ISync sync in syncs)

    {

        sync.Acquire();

    }

    pool.Close();

}

16.5.3. Using the executor to do a parallel grep

构建的执行者的使用相当的直接,但是如果我们想要真正的使拥池还是有一点麻烦:

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();
}

 

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();
}

 

16.6 Aop

参见第17AOP指南

原创粉丝点击