软件架构表示整个软件的结构,与数据组织与执行流程相关,它反映设计思想、开发方法学以及域模型。
1. 多线程架构
多线程软件架构是一种将工作模式分解为两个或更多并发执行线程的软件架构。分解软件是分割为单独逻辑任务的过程,供软件的工作模式来执行,其中部分任务分配给不同的执行线程,当这些线程允许同步或并发地在单进程内执行时,软件就具有多线程架构。
对话(session)为最大的执行单元,它包含一个或多个可以并发执行的进程。当多个进程并发执行时,对话就是多任务或多进程。一个进程包含一个或多个线程。当一个进程内有两个或更多线程并发执行时,进程就是多线程(或称多线程化)。一个线程包含一条或多条指令,当一个线程内包含一条或多条并发执行的指令时,这些指令共同形成了一个算法,我们称这套指令为并行算法(parallel algorithm)。
2. 使用多线程的常见架构
在应用程序中使用多线程处理的目标之一是精选出具有内在并发性和并行性的架构。通过这种方式,软件开发者不必强迫应用程序的逻辑使用多线程化模型。至于多线程处理,功能必须服从形式。应当搜寻和使用那些天生就适合于多线程处理的架构。如果对于研究域不存在这些的架构,则必须创建这样的架构。有一些天生就适合于多线程处理的常见架构,如下是其中的三种。
a. 客户机/服务器; b. 事件驱动编程(GUI); c. 黑板(blackboard)。
C/S范例将工作模式分为由进程或线程表示的两端。一端(即客户机)请求数据或动作,另一端(即服务器)完成该请求。
3. 黑板架构
黑板是一种问题解决结构,与专家系统一样,黑板设计用于解决特定的问题,由此生成的解决方案也只针对该问题才有用。
黑板架构由两个基本组件构成: a. 黑板数据结构; b. 知识源。
通过知识源,在问题解决过程中协作使用黑板。知识源是一个具有特定域过程化知识、陈述性知识或两者兼有的软件组件。它也包含一个或多个推理引擎,可以工作于所包含的过程化或陈述性知识之上。
黑板模型是多线程技术的理想之选,它的临界区可以受互斥量和条件变量的保护。可以将每个知识源分配给一个单独的线程。
知识源所执行的处理过程可能要消耗很多的处理器时间。就像在逻辑服务器中一样,知识源可以执行演绎、归纳或推测。这种推理可能包含上千次推论与假设过程。我们并不希望序列化在这种条件下知识源所做的处理过程。特别是,一个知识源的输入可能是另一个知识源的输出。它们经常重复和递归性地一起工作。有时,它们也可能以一个分享的方式工作。不过,唯一发生的交互就是通过黑板数据结构,它是一个临界区。
4. 途径上的不同(面向对象与过程化)
过程化编程方式的特点是在系统开发中使用自顶向下(top-down)、逐步求精法(stepwise refinement approach)。在自顶向下方式中,系统或应用程序用单个主过程来描述。然后将这个过程分解成逻辑子过程(logical subprocedure)。这些逻辑子过程被进一步分解成逻辑子过程。这个分解过程一直持续到整个应用程序能够在程序语句级进行描述。在过程化编程方式中,将应用程序看作过程和函数的集合。
在过程化编程中,在将应用程序分解为函数部分时,几乎不考虑所使用的数据结构和数据库。数据只被看作应用程序中过程和函数所使用的对象而已。应用程序以过程为中心。在多线程应用程序中的主要缺陷是竞争条件和死锁,它们都以数据和资源为焦点,而正是临界区(数据)和资源(数据和设备)的误用才导致多线程序应用程序出现问题。多线程的过程化方式因为是以函数为中心,而不是以数据为中心,所以才存在缺陷。
在多线程应用程序中,任何非常量数据都有可能成为一个临界区。在中型到大型的多线程应用程序中,如何才能成功管理临界区以及其它共享资源呢?可以使用面向对象编程技术来解决多线程应用程序中大部分管理临界区及其它共享资源的问题。适当使用封装来组合互斥类和事件类与即将在多线程环境中使用的所有类,这是控制竞争条件的最有效可行方法之一。
我们可以使用C++的封装机制提高多线程应用程序的可靠性,其途径至少有以下两种:
a. 在C++类中封装互斥量和条件变量,只提供成员函数的访问权限;
b. 通过继承或复合结合互斥量或条件变量类与宿主类。
当多线程处理面向对象架构时,转移责任给我们带来了一个重要的途径。同步对象访问以及保护对象临界区的责任应用从对象的用户身上转移到对象的提供者身上。对象的用户不用费心去规划对象具有的临界区,在使用私有属性时,对象的提供者必须提供同步和保护措施。
对于可能应用于多线程环境中的类,我们必须包含对象访问策略。提供对多线程处理的支持以及对类的临界区的保护是类提供者的一部分责任。如果打算将类用于多线程应用程序中,则必须为类中的临界区指定访问策略。类的设计者必须提供类的相应并发模型。
为了开发支持并发的面向对象架构,C++程序员可以首先,在一套低层类中封装特定系统的功能性,低层类例如线程类和互斥类,同时,在低层类的上层构建高层类。最后,最高层类的用户中只能看到一般性的并发模型。
5. 增量多线程处理
一旦设计者为类选定了并发访问策略,必须相应地设计该类的成员函数,使得类用于多线程环境中时,强制实施并发访问策略。这要求与类连接的同步类应当用于正确保护临界区。它还要求设计者将类的数据组件分割成可以并发修改和访问的部分以及不能如此做的部分。锁定和取消锁定通过所需的成员函数来完成。用信号通知和广播由所需的成员函数来完成。通过类成员函数实现类的访问策略后,该类就可以用作一个任务类、线程类或应用框架中的组件了。
在设计方,它从选定适宜于多线程处理的架构开始,一旦选定了架构,然后设计应用框架。应用框架设计完毕,再设计支持应用框架的类库和容器类。类库、容器类和域类都是基于同步类而构建。
在实现方,从最低层的类开始,向最高层类推进。它是一种构建块途径来多线程处理C++组件。它不是试图强迫应用于多个线程,而是选定一个自然而然适宜于多线程处理的架构。然后,我们用一些提供了同步和协调的组件来填充这些架构。