10.5.2 代码不能正常工作该怎么办
例如,执行了最基本的单元测试后,假设您发现程序清单10-1中的程序未通过单元测试。程序不能正常工作。程序不能正常工作意味着什么?这个程序失败的原因是它不符合PBS的规范。这意味着程序使PBS中的一条或多条陈述为假。由于PBS目的是捕捉并发底层结构的含义,您知道程序中的并行性将会在某一位置上失败,因为它没有正确地实现PBS中的断言、陈述和谓词。特别是PBS的第6条。
分解6:如果编码每15秒改变一次,需要4N个代理2分钟内从四百万个编码的样本中找到正确的编码。
回顾程序清单10-1中第13行~第15行引用的用户定义谓词valid_code( )。它说明搜索是否成功。但为了使搜索成功,PBS中所有的断言都必须被保持。如果仔细看一下程序清单10-3中第9行~第30行的用于valid_code的谓词operator( ),您会发现产生了4个posix_process。程序清单10-4给出了用户定义的process_interface类的定义。
程序清单10-4
- // Listing 10-4 Definitions for user-defined posix_process interface
- // class.
-
- 1 #include "posix_process.h"
- 2 #include <sys/wait.h>
- 3
- 4
- 5 posix_process::posix_process(string Path,char **av,char **env)
- 6 {
- 7
- 8 argv = av;
- 9 envenvp = env;
- 10 ProgramPath = Path;
- 11 posix_spawnattr_init(&SpawnAttr);
- 12 posix_spawn_file_actions_init(&FileActions);
- 13
- 14
- 15 }
- 16
- 17 posix_process::posix_process(string Path,char **av,char **env,
- posix_spawnattr_t X,
- posix_spawn_file_actions_t Y)
- 18 {
- 19 argv = av;
- 20 envenvp = env;
- 21 ProgramPath = Path;
- 22 SpawnAttr = X;
- 23 FileActions = Y;
- 24 posix_spawnattr_init(&SpawnAttr);
- 25 posix_spawn_file_actions_init(&FileActions);
- 26
- 27
- 28
- 29 }
- 30
- 31 void posix_process::run(void)
- 32 {
- 33
- 34 posix_spawn(&Pid,ProgramPath.c_str(),&FileActions,
- &SpawnAttr,argv,envp);
- 35
- 36
- 37 }
- 38
- 39 void posix_process::pwait(int &X)
- 40 {
- 41
- 42 wait(&X);
- 43 }
程序清单10-4中的类是为POSIX API posix_spawn( )适配接口的接口类。posix_process类用来产生两个ofind_code的副本。ofind_code依次产生两个线程。问题是最终您仅得到8个连续的agent(4进程×2线程)开展工作。因此,如果编码被改变,PBS中的第6条要求您将agent的数目加到4倍,这不是由valid_code谓词解决的。在这种情况下,您可以简单地将每个agent映射到一个线程。如果应用程序中的谓词与PBS不一致,那么您将不会通过单元测试。
这种将PBS同C++(www.cppentry.com)中的谓词匹配起来是转向声明式风格的并行编程(www.cppentry.com)的基础的一部分。这是一个细微的但非常强大的想法。您将软件验证与确认(V&V)应用到PBS的映射,然后C++(www.cppentry.com)谓词将并行编程(www.cppentry.com)的声明式风格扩展到V&V。V&V的其他重要目的是:
推动软件错误的早期检测与改正
增强对过程和产品风险的深入管理
支持软件生命周期处理,以确保符合程序性能、进度和预算需求
如果根据PBS对谓词进行审计,可以在软件部署之前较早地发现并发问题。实际上,PADL和PBS的主要目的之一就是在书写任何代码之前处理并发需求的全部复杂性。
PADL和PBS的后果之一就是它们支持测试的声明式模型,如模型检测和可能世界分析(将会在本章稍后部分简要介绍)。这些模型可在SDLC的早期部署。在软件开发工作的初期,就必须对多线程和多处理程序的复杂性和完整性进行管理。测试过程必须反映出这一点。请看来自IEEE Std 1012的如下内容:
在规划V&V过程时,软件的完整性级别通常在开发过程的早期指定,更好的做法是在系统需求、分析及架构设计活动中指定。软件的完整性级别可被指定给软件需求、功能、功能组或软件组件及子系统。被指定的软件完整性级别可能会随着软件的演变而变化。开发机构选择的设计、编码、程序上的、技术上的实现特性可以提升或降低软件的危险程度和指定给软件的相关软件完整性级别。
V&V在单元测试级别上非常有效(如果被使用),如同ValidCode( )谓词在单元测试级别上失败的例子所说明的。单元测试过程由共被分为8个基本活动的3个阶段组成:
(1) 进行测试计划编制
a. 规划总体方法、资源和进度
b. 确定要进行测试的特性
c. 精化总体规划
(2) 获取测试集
a. 设计测试集
b. 实现精化后的计划和设计
(3) 测量测试单元
a. 执行测试过程
b. 检查终止
c. 评估测试投入和单元
我们建议您在PADL模型中的第4层分析结束及PBS完成后,开始将这些基本活动集成起来。表10-5中列出了应当在第4层分析的测试阶段定位的其他常见错误类型。
表10-5
|
错误的种类< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> |
描 述 |
|
用户接口错误 |
用户接口的设计、功能或性能方面的错误;
用户接口可能会在通信、命令结构或输出等
方面失败;程序可能会无法完成用户期望的任
务或执行任务时非常笨拙 |
|
边界相关的错误 |
程序的边界是指使得程序改变其行为的任何
事;错误地描述边界条件或未能检测到程序的
限制都会产生错误 |
|
计算错误 |
计算期间发生的错误;这些错误包括曲解公式
或丢失精度;它还包括由于使用不正确的算
法导致的计算上的错误 |
|
初始和后续的状态 |
初次使用程序时发生的错误;每次程序重新
启动时都会发生这些错误 |
|
竞争条件 |
当一个线程或进程在期望的线程或进程之
前执行时发生的错误 |
|
控制流错误 |
当程序执行错误的后续步骤时发生的错误 |
|
错误处理 |
当处理错误时产生的错误;程序可能无法检测
或预料可能的错误,并且无法合理地改正这些错误 |
|
处理或解释数据中的错误 |
当数据在模块、程序、线程、进程间来回
传递时,被曲解、毁坏或丢失时发生的错误 |
|
负荷状态 |
当程序超载时发生的错误;程序在长期执行很
多工作或突然完成大量工作时可能会发生错
误;程序可能会在内存不足、与其他程序或
例程共享的资源耗尽时失败;程序到达其限制
时被执行的状态如何?当程序超出其限制
时会执行得多差? |
|
硬件 |
当程序不能识别硬件故障或无法从硬件故障中
恢复时发生的错误;程序可能无法识别从设
备返回的错误编码,或者可能试图访问不
存在的或已经被使用中的设备;程序也
可能向设备发送错误的数据 |
|
源和版本控制 |
当使用不正确版本的程序时发生的错误;
有可能包含错误的较旧版本的子过程被
链接到程序的最新版本 |
|
文档 |
当使用错误的文档时发生的错误 |
|
测试错误 |
在软件测试期间发生的错误 |
使用应用程序的声明式架构,加上将PBS和PADL映射到单元测试阶段的早期,可以处理并行编程(www.cppentry.com)开发工作中的最重要的挑战。这项技术被使用C++(www.cppentry.com)异常处理的应用程序所支持,该技术被称作逻辑容错。