多线程为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦。线程间同步、数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误。另外,应用逻辑和线程逻辑纠缠在一起,会导致程序的逻辑结构混乱,难以复用和维护。本文试图给出一个解决这个问题的方案,通过构建一个并发模型框架(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没有需要保护的数据,但是随着需求的变化,以后可能会有的)。但是这样做至少会存在以下几个问题:
-
现在要维护ServiceImp的两个版本:多线程版本和单线程版本(有些地方,比如其他项目,可能没有并发的问题),容易带来同步更新和正确选择版本的问题,给维护带来麻烦。
-
如果多个并发的Client频繁调用该服务,由于是直接同步调用,会造成Client阻塞,降低服务质量。
-
很难进行一些灵活的控制,比如:根据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并发模型框架,其中使用了一些构建框架的常用技术,当然所构建的框架和一些成熟的商用框架相比,显得非常稚嫩,比如没有考虑服务调用有返回值的情况,但是其思想方法是一致的,希望读者能够深加领会,这样无论对于构建自己的框架还是理解一些其他的框架都是很有帮助的。读者可以对本文中的框架进行扩充,直接应用到自己的工作中。下面列出本框架的优缺点:
优点:
-
增强了应用的并发性,简化了同步控制的复杂性
-
服务的请求和服务的执行分离,使得可以对服务请求排队,进行灵活的控制
-
应用逻辑和并发模型分离,使得程序结构清晰,易于维护、重用
-
可以使开发者集中精力于应用领域
缺点:
-
由于框架所需类的存在,在一定程度上增加了程序的复杂性
-
如果应用需要过多的活动对象,由于线程切换开销会造成性能下降
-
可能会造成调试困难
PS:原文录自UML软件工程组织,是讲Java的异步框架的,我这里将其简化了一下。
分享到:
相关推荐
本文试图给出一个解决这个问题的方案,通过构建一个并发模型框架(framework),使得开发多线程的应用变得容易。基础知识Java语言提供了对于线程很好的支持,实现方法小巧、优雅。对于方法重入的保护,信号量...
2. **构架建模惯用法**:为了确保构架模型的一致性和清晰性,通常会采用一套固定的建模惯用法。例如,在用例视图中,用例通常会被命名为描述性的动作短语,以便于理解其功能。 #### 八、结论 综上所述,技术框架在...
### 对象持久类框架的构架设计 #### 一、概念定义 在深入探讨对象持久类框架的构架设计之前,我们首先明确几个关键概念: 1. **世界**:指的是系统内部的所有元素,包括但不限于数据库、数据库记录、对象实例等。...
这涉及到创建一个基本的游戏框架,该框架能够支持象棋游戏的实现。通过理解这种简单的游戏架构,开发者不仅可以掌握构建棋类游戏的基础,还能进一步扩展到其他类似的小游戏。 首先,让我们了解什么是游戏架构。游戏...
2. **企业级应用程序的特点**: 企业级应用通常需要处理大量数据、高并发用户、复杂业务逻辑以及严格的性能和安全性需求。因此,理解这些特性是成功构架的关键。 3. **设计模式**: 设计模式如MVC(模型-视图-控制器...
1. **并发模型**: 如何管理并发任务,包括线程池的设计、锁机制的选择等。 2. **资源管理**: 如何高效地管理和释放系统资源。 3. **错误处理**: 错误处理策略,包括异常捕获和日志记录。 4. **负载均衡**: 如何根据...
"DEMO-多循环应用程序构架.rar_DEMO_labview多线程框架"是一个专门为初学者设计的示例,它展示了如何在LabVIEW中构建一个多线程应用。LabVIEW,全称Laboratory Virtual Instrument Engineering Workbench(实验室...
Spring框架是Java领域的企业级应用开发的首选,提供了全面的开发支持,包括依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)等。其他如Docker和Kubernetes则在容器化和集群管理方面扮演重要角色。 最后,...
- **NIO(Non-blocking I/O)**:Netty基于Java NIO API构建,提供非阻塞的I/O操作,提高了系统资源利用率,尤其适合高并发场景。 - **EventLoop**:Netty的核心组件,负责处理I/O事件,将它们分发到相应的...
.NET框架提供了ThreadPool、Task Parallel Library(TPL)以及异步编程模型,帮助开发者编写高效的多线程代码。 8. 应用程序部署和生命周期管理:理解.NET应用程序的部署策略,包括ClickOnce部署和NuGet包管理,...
通过阅读《精通.Net核心技术原理与构架》,读者可以深入了解.NET Framework的各个层面,从基础的内存管理、类型系统到复杂的并发控制、分布式应用开发,全面提升.NET开发技能。书中的实例和实战指导将帮助读者将理论...
在多线程方面,书籍会深入讨论并发编程的关键概念,如线程创建与同步、锁与 Monitor、线程池以及异步编程模型(如Task和async/await关键字)。多线程和异步编程是现代应用程序中不可或缺的部分,能有效利用多核...
7. **命令行工具**:Yii提供的Gii代码生成工具,可以快速生成模型、控制器、视图等基础代码,提高开发效率。 8. **RESTful API支持**:Yii2为构建RESTful Web服务提供了强大的支持,便于实现前后端分离和移动应用...
- **高性能**:优化了性能瓶颈,确保高并发下的稳定表现。 **2. 目录结构** KISS FRAMEWORK的目录结构清晰合理,主要包括以下几个部分: - **core**:框架核心文件,包括框架的基础类库和核心功能实现。 - **...
在本示例“DEMO-多循环应用程序构架.rar”中,我们重点关注的是LabVIEW中的多循环架构模式,这是一种高级编程技术,常用于实现复杂的并发任务。此Demo旨在帮助用户了解并熟练掌握如何在LabVIEW中运用生产者-消费者...
本教程将详细讲解一个基于Python的Django框架构建的跑酷游戏服务器构架,旨在为开发者提供学习和参考的实例。 Django是一个高级的、快速的、全功能的Web开发框架,它遵循模型-模板-视图(Model-Template-View,MTV...
8. **多线程和并发**:.NET 提供了丰富的 API 支持多线程编程,包括线程池、同步原语、异步编程模型(如 async/await)等,帮助开发者构建高性能的并发应用。 9. **Windows Presentation Foundation (WPF)**:一个...
在企业门户网站中,Access可以作为初期的数据存储解决方案,但因其性能和并发能力的限制,大型或高并发的系统可能会选择更强大的数据库系统,如SQL Server或MySQL。 【学习要点】 1. **AjaxHelper使用**:掌握如何...