并发模式&& 主动对象 读书笔记转

并发模式

为在多线程或进程间共享资源而设计的2种模式:

      ·主动对象设计模式将方法执行和方法调用分离开来。目的是加强并发和简化对驻留在自身控制线程中的对象的同步访问。

      ·监视器对象设计模式同步化并发方法的执行,以确保同一时刻在对象内部只有一个方法运行。它也允许对象的方法协作调度方法的执行顺序。

下面2种模式定义了高层并发体系结构:

      ·半同步/半异步体系结构模式将并发系统中的同步和异步进程分离,简化了编程而又不影响性能。该模式引入两个通信层,一个处理同步服务,另一个处理异步服务。还有一个排队层协调异步层和同步层服务之间的通信。

      ·领导者/追随者体系结构模式提供了一种高效的并发模型,多个线程依次共享一组事件源,从而对事件源中产生的服务请求进行检测、多路分解、分配和处理。对由缓冲池的线程进行处理的请求没有同步化或没有排序的约束时,领导者/追随者模式可以代替半同步/半异步和主动对象模式,以改进性能。

为解决某些并发的内在复杂性提供另一种策略: 

      ·线程特定的存储器设计模式允许多个线程使用一个“逻辑上全局的”访问点检索某一本地对象,而不会导致访问对象时发生加锁开锁。在某种程度上,这种模式可以看做其他模式的“对立面”,因为它通过防止进程间的资源共享来实现复杂的内部并发机制。

5.1主动对象(Active Object)

1.问题

      许多应用程序受益于使用并发对象而改进了服务质量,然而,对象并发运行时,如果这些对象被多个客户机线程共享和修改,则必须同步化对它们的方法和数据的访问,这时产生三个强制条件:

      1)并发地调用对象的处理密集的方法不应无限期地阻塞整个进程,并因些降低其他并发对象的服务质量。

      2)应该很容易编程实现对共享对象的同步化访问。特别地,对共享对象的客户机方法调用的串行化和调度都应是透明的,该共享对象具有同步约束。

      3)应用程序的设计应透明地使用在硬件/软件平台提供的并行机制。

2.解决方案

      对受以上强制条件约束的对象而言,将其方法调用与执行分离。方法调用应该发生在客户机的控制线程中,而方法执行应发生在另一个线程中。此外,要将这种分离设计成使客户机线程看上去像调用普通的方法一样。

      细节:代理(Proxy)代表主动对象的接口。服务者(Servant)提供了主动对象的实现。代理和服务者在不同的线程中运行,因此方法调用和方法执行可以并发执行。代理在客户机线程中运行,而服务者在另一个线程中运行。

      在运行时代理将客户机的方法调用转换为方法请求,调度程序将方法请求存储在一个激活表(Activation List)中。调度程序的事件循环和服务者在同一线程中连续运行,将方法请求从激活表队列中取出并将它们分配给服务者。客户机可以通过代理返回的前景得到方法执行的结果。

3.结构

      代理提供一个接口允许客户机调用主动对象的公共可访问的方法。代理的使用允许应该程序使用标准的强类型语言特性编程,而不是在线程间传递弱类型的消息。代理驻留在客户机线程中。

      客户机调用一个由代理定义的方法时,它触发方法请求(Method Request)对象的构造函数方法请求包括语境信息(如方法的参数),执行指定方法调用和向客户机返回结果时需要这些信息。方法请求类定义了一个执行主动对象方法的接口。接口也包括可以用来确定某一方法请求是否可执行的哨兵方法。对由主动对象中需要同步化访问的代理所提供的每个公共方法,从方法请求类中派生出具体方法请求类(Concrete MethodRequest)。

       

       

      代理将它建立的具体方法请求插入一个激活表(Activation List)。该表包含一个待处理的由代理建立的方法请求组成的有限缓冲区,并跟踪哪些方法请求可执行。激活表将驻留代理的客户机线程和执行服务的线程分开,这样两个线程可以同时运行。因此,激活表的内部状态必须串行化,以防对它同时访问。

      调度程序(Scheduler)在其客户机代理之外的线程中运行,也就是主动对象的线程。它决定下一次对主动对象将执行哪一个方法请求。这一调度决策基于不同的准则(如排序一对主动对象调用方法的顺序),或者基于主动对象的某些属性(如它的状态)。调度程序可以使用方法请求哨兵评估这些属性,方法请求哨兵确定何时可以执行这些方法请求。调度程序用激活表管理待处理的方法请求。当客户机调用其方法时,代理将方法请求插入激活表中。

      服务者被建模为主动对象,其中定义了其行为和状态。服务者实现的方法对应于代理接口和由代理建立的方法请求。服务者可能还包含其他谓词方法,方法请求能够用这些谓词方法类实现哨兵。在调度程序执行与之关联的方法请求时调用服务者方法。因而,它在调度程序的线程中执行。

      当客户机调用代理上的方法时,会接收到一个前景(future)。该前景允许客户机在服务者完成方法的执行后获得方法调用的结果。每个前景都为被调用的方法保留一定的空间,存放方法调用的结果。当客户机想得到该结果时,它可以与前景汇合(rendezvous),阻塞或轮询直到结果被计算出来并存入前景。 

       

       

4.实现

1)实现服务者。服务者被建模为主动对象,其中定义了其行为和状态。另外,服务者还可能包含用来确定何时执行方法请求的谓词方法。

2)实现调用的基础设施。在这一活动中,描述了客户机调用主动对象的方法所必需的基础设施。这种基础设施由建立方法请求的代理构成。

  2.1)实现代理。代理为客户机提供了访问服务者方法的接口。对于客户机的每次方法调用,代理都建立一个具体的方法请求。每一方法请求是方法语境的抽象,也称为方法闭包(closure)。一般情况下该语境包括方法参数,到被方法应用的服务者的绑定,结果的前景以及执行方法请求的代码。

  2.2)实现方法请求。方法请求可以看做一个命令对象,方法请求类声明了一个被所有具体方法请求使用的接口,它为调度程序提供了一个统一的接口,而不用了解如何评估同步约束或者如何触发具体方法请求的执行。

3)实现激活表。每个方法请求都插入到一个激活表中。该表可以实现为同步限界缓冲区,该缓冲区由客户机线程和运行主动对象调度程序及服务者的线程共享。激活表也可以提供一个健壮的迭代器,调度程序可以遍历和删除它的元素。

4)实现主动对象的调度程序。调度程序是一个命令处理程序。用于管理激活表和执行满足同步约束的未处理的方法请求。调度程序的公共接口通常提供两个方法,一个被代理用于将方法请求插入到激活表中。另一个用于将方法请求分配给服务者。

5)确定汇合和返回值策略。汇合策略确定客户机如何从对主动对象调用的方法中得到返回值。在一个线程中执行的主动对象服务者向另一个线程中运行的调用该方法的客户程序传递返回值时,汇合发生。实现主动对象模式通常选择如下汇合和返回值策略:

  ·同步等待。在代理中同步阻塞客户机线程,直到调度程序分配方法请求并且得到结果并存储到前景中。

  ·同步定时等待。阻塞有限的时间,并且如果主动对象的调度程序在规定时间内未能分配方法请求则失败。如果超时为零,客户机线程就“轮询”,也就是在调度程序不能将它立即分配时可返回到调用者而不需将方法请求放入队列。

  ·异步。将方法调用放入队列并且立即将控制返回给客户机。如果方法是一个产生结果的双向调用,则必须使用某些形式的前景以提供对值的同步化访问,或者在调用失败时对错误状态的同步化访问。

5.结论

优点:

1)增强了应用程序的并发性,简化了同步的复杂性。通过允许客户机线程和异步方法同时执行而增加了应用程序的并发性。通过使用调度程序而简化了同步的复杂性,调度程序计算同步约束,以保证根据其状态对服务者进行串行访问。

2)透明地应用可用的并行性。如果硬件和软件平台能有效地支持多CPU,那么该模式可以允许多个主动对象并行地执行,是否并行执行仅仅取决于它们的同步约束。

3)方法的执行顺序可以和方法的调用顺序不同。异步调用的方法是根据由方法的哨兵和调度策略定义的同步机制而执行的。因此,方法执行的顺序可以与方法调用的顺序不同。这种分离有助于提高应用程序的性能和灵活性。

不足:

1)性能开销。根据主动对象的调度程序的实现方式不同,当调度和执行主动对象方法调用时可能引起语境切换,同步化和数据移动开销。一般来说,主动对象模式最适用于相对粗粒度的对象。

2)复杂的调试。因为各种主动对象的调度程序和底层的操作系统线程调度程序的并发性和不确定性。所以很难调试使用主动对象模式的程序。 

 例子就是:

  主动对象模式用于降低方法执行和方法调用之间的耦合。该模式描述了另外一种更为透明的任务间通信方法-------> cas  无锁队列。

  传统上,所有的对象都是被动的代码段,对象中的代码是在对它发出方法调用的线程中执行的,当方法被调用时,调用线程将阻塞,直至调用结束。而主动对象却不一样。这些对象具有自己的命令执行线程,主动对象的方法将在自己的执行线程中执行,不会阻塞调用方法。

  例如,设想对象"A"已在你的程序的main()函数中被实例化。当你的程序启动时,OS创建一个线程,以从main()函数开始执行。如果你调用对象A的任何方法,该线程将"流过"那个方法,并执行其中的代码。一旦执行完成,该线程返回调用该方法的点并继续它的执行。但是,如果"A"是主动对象,事情就不是这样了。在这种情况下,主线程不会被主动对象借用。相反,当"A"的方法被调用时,方法的执行发生在主动对象持有的线程中。另一种思考方法:如果调用的是被动对象的方法(常规对象),调用会阻塞(同步的);而另一方面,如果调用的是主动对象的方法,调用不会阻塞(异步的)。

  由于主动对象的方法调用不会阻塞,这样就提高了系统响应速度,在网络编程中是大有用武之地的。

最直接的例子是:"Logger"(日志记录器)对象对象为例来介绍如何将一个传统对象改造为主动对象,从而提高系统响应速度。

原文地址:https://www.cnblogs.com/codestack/p/15223716.html