构架并发模型框架

多线程为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦。线程间同步、数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误。另外,应用逻辑和线程逻辑纠缠在一起,会导致程序的逻辑结构混乱,难以复用和维护。本文试图给出一个解决这个问题的方案,通过构建一个并发模型框架(framework),使得开发多线程的应用变得容易。

简单例子

本文将围绕一个简单的例子展开论述,这样可以更容易突出我们解决问题的思路、方法。本文想向读者展现的正是这些思路、方法。这些思路、方法更加适用于解决大规模、复杂应用中的并发问题。

考虑一个简单的例子,我们有一个服务提供者,它通过一个接口对外提供服务,服务内容非常简单,就是在标准输出上打印Hello World。类结构图如下:

 代码如下:

using System;
using System.Collections.Generic;
using System.Text;

using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            IService serv = new ServiceImp();
            Client client = new Client(serv);
            client.requestService();
        }
    }

    interface IService
    {
        void SayHello();
    }

    class ServiceImp : IService
    {
        public void SayHello()
        {
            Console.WriteLine("ServiceImp");
        }
    }

    class Client
    {
        IService serv;
        public Client(IService serv)
        {
            this.serv = serv;
        }

        public void requestService()
        {
            serv.SayHello();
        }
    }
}

如果现在有新的需求,要求该服务必须支持Client的并发访问。一种简单的方法就是在ServicImp类中的每个方法前面加上synchronized声明,来保证自己内部数据的一致性(当然对于本例来说,目前是没有必要的,因为ServiceImp没有需要保护的数据,但是随着需求的变化,以后可能会有的)。但是这样做至少会存在以下几个问题:

  1. 现在 要维护ServiceImp的两个版本:多线程版本和单线程版本(有些地方,比如其他项目,可能没有并发的问题),容易 带来同步更新和正确选择版本的问题,给维护带来麻烦。 
  2. 如果多个并发的Client频繁调用该服务,由于是直接同步调用,会造成Client 阻塞,降低服务质量。 
  3. 很难进行一些灵活的控制,比如:根据Client的优先级进行排队等等。

这些问题对于大型的多线程应用服务器尤为突出,对于一些简单的应用(如本文中的例子)可能根本不用考虑。本文正是要讨论这些问题的解决方案,文中的简单的例子只是提供了一个说明问题,展示思路、方法的平台。

如何才能较好的解决这些问题,有没有一个可以重用的解决方案呢?让我们先把这些问题放一放,先来谈谈和框架有关的一些问题。

框架概述

熟悉 面向对象的读者一定知道面向对象的最大的优势之一就是:软件复用。通过复用,可以减少很多的工作量,提高软件开发生产率。复用本身也是分层次的,代码级的复用和设计架构的复用。

大家可能非常熟悉C语言中的一些标准库,它们提供了一些通用的功能让你的程序使用。但是这些标准库并不能影响你的程序结构和设计思路,仅仅是提供一些机能,帮助你的程序完成工作。它们使你不必重头编写一般性的通用功能(比如printf),它们强调的是程序代码本身的复用性,而不是设计架构的复用性。

那么什么是框架呢?所谓框架,它不同于一般的标准库,是指一组紧密关联的(类)classes,强调彼此的配合以完成某种可以重复运用的设计概念。这些类之间以特定的方式合作,彼此不可或缺。它们相当程度的影响了你的程序的形貌。框架本身规划了应用程序的骨干,让程序遵循一定的流程和动线,展现一定的风貌和功能。这样就使程序员不必费力于通用性的功能的繁文缛节,集中精力于专业领域。

有一点必须要强调,放之四海而皆准的框架是不存在的,也是最没有用处的。框架往往都是针对某个特定应用领域的,是在对这个应用领域进行深刻理解的基础上,抽象出该应用的概念模型,在这些抽象的概念上搭建的一个模型,是一个有形无体的框架。不同的具体应用根据自身的特点对框架中的抽象概念进行实现,从而赋予框架生命,完成应用的功能。

基于框架的应用都有两部分构成:框架部分特定应用部分。要想达到框架复用的目标,必须要做到框架部分和特定应用部分的隔离。使用面向对象的一个强大功能:多态,可以实现这一点。在框架中完成抽象概念之间的交互、关联,把具体的实现交给特定的应用来完成。其中一般都会大量使用了Template Method设计模式。

Java中的Collection Framework以及微软的MFC都是框架方面很好的例子。有兴趣的读者可以自行研究。

构建框架

如何构建一个并发模型框架呢?让我们先回到原来的问题,先来分析一下原因。造成要维护多线程和单线程两个版本的原因是由于把应用逻辑和并发逻辑混在一起,如果能够做到把应用逻辑和并发模型进行很好的隔离,那么应用逻辑本身就可以很好的被复用,而且也很容易把并发逻辑添加进来而不会对应用逻辑造成任何影响。造成Client阻塞,性能降低以及无法进行额外的控制的原因是由于所有的服务调用都是同步的,解决方案很简单,改为异步调用方式,把服务的调用和服务的执行分离。

本框架的核心就是使用主动对象来封装并发逻辑,然后把Client的请求转发给实际的服务提供者(应用逻辑),这样无论是Client还是实际的服务提供者都不用关心并发的存在,不用考虑并发所带来的数据一致性问题。从而实现应用逻辑和并发逻辑的隔离,服务调用和服务执行的隔离。

我们可以通过主动对象实现对并发逻辑的封装。开发者只需要根据需要实现MethodRequest接口,另外再定义一个服务代理类提供给使用者,在服务代理者类中把服务调用者的请求转化为MethodRequest实现,交给活动对象即可。

使用该框架,可以较好的做到应用逻辑和并发模型的分离,从而使开发者集中精力于应用领域,然后平滑的和并发模型结合起来,并且可以针对ActiveQueue定制排队机制,比如基于优先级等。

基于框架的解决方案

本小节将使用上述的框架重新实现前面的例子,提供对于并发的支持。第一步先完成对于IMethodRequest的实现,对于我们的例子来说实现如下:

class SayHelloMethod : IMethodRequest
{
    public SayHelloMethod(IService serv)
    {
        this.serv = serv;
    }

    public void Call()
    {
        serv.SayHello();
    }

    private IService serv;
}

该类完成了对于服务提供接口sayHello方法的封装。接下来定义一个服务代理类,来完成请求的封装、排队功能,当然为了做到对Client透明,该类必须实现Service接口。定义如下:

class ServiceProxy : IService
{
    public ServiceProxy()
    {
        _service = new ServiceImp();
        _active_object = new ActiveObject();
    }

    public void SayHello()
    {
        IMethodRequest mr = new SayHelloMethod(_service);
        _active_object.AddCommand(mr);
    }

    private IService _service;
    private ActiveObject _active_object;
}

其他的类和接口定义不变,下面对比一下并发逻辑增加前后的服务调用的变化,并发逻辑增加前,对于sayHello服务的调用方法:

IService s = new ServiceImp();
Client c = new Client(s);
c.requestService();

并发逻辑增加后,对于sayHello服务的调用方法:

IService s = new ServiceProxy();
Client c = new Client(s);
c.requestService();

可以看出并发逻辑增加前后对于Client的ServiceImp都无需作任何改变,使用方式也非常一致,ServiceImp也能够独立的进行重用。类结构图如下:

读者容易看出,使用框架也增加了一些复杂性,对于一些简单的应用来说可能根本就没有必要使用本框架。希望读者能够根据自己的实际情况进行判断。

结论

本文围绕一个简单的例子论述了如何构架一个Java并发模型框架,其中使用了一些构建框架的常用技术,当然所构建的框架和一些成熟的商用框架相比,显得非常稚嫩,比如没有考虑服务调用有返回值的情况,但是其思想方法是一致的,希望读者能够深加领会,这样无论对于构建自己的框架还是理解一些其他的框架都是很有帮助的。读者可以对本文中的框架进行扩充,直接应用到自己的工作中。下面列出本框架的优缺点:

优点:

  1. 增强了应用的并发性,简化了同步控制的复杂性 
  2. 服务的请求和服务的执行分离,使得可以对服务请求排队,进行灵活的控制 
  3. 应用逻辑和并发模型分离,使得程序结构清晰,易于维护、重用 
  4. 可以使开发者集中精力于应用领域

 缺点:

  1. 由于框架所需类的存在,在一定程度上增加了程序的复杂性 
  2. 如果应用需要过多的活动对象,由于线程切换开销会造成性能下降 
  3. 可能会造成调试困难

PS:原文录自UML软件工程组织,是讲Java的异步框架的,我这里将其简化了一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值