利用数学证明方式验证程序的可靠性(一)

来源:互联网 发布:拙心网络企业电话 编辑:程序博客网 时间:2024/05/19 18:40
                                                              利用数学证明方式验证程序的可靠性(一)
在编写后台程序的时候,我们常常利用压力测试来验证用于处理任务队列的特定算法是否存在Bug。但是如果测试的压力程度没有达到这个Bug出现的下限,因此我们经常在使用软件的时候面临灾难。
 灾难不是来自我们的惰性,而是来自压力程序的程度。
 因此,在验证程序的可靠性时候,我习惯尝试使用数学证明方式来验证。 在处理数学问题的时候,我们往往知道一个数学问题得以解决的关键条件是什么,也因此可以知道问题解决失败的原因。同样利用这种方式,可以让我们知道程序出现bug的条件是在哪里。
 或者换句话说,一个程序被测试验证是可靠的,它的条件是什么,其不可靠的条件是什么。
 然而怎么证明某个特定算法的可靠性呢?
首先简化程序,提出一个模型。最近在工作过程中,遇到一个这样的问题:一个客户端定时向服务器添加一序列分配给某些客户端(C1…….Cn)执行的呼叫任务,该序列呼叫任务是具有某个呼叫计划标志。而服务端将该呼叫计划标志添加到C1……Cn呼叫队列中,该呼叫队列是一个特定类型数组Cj_Work[Max]( Cj_Work是客户端Cj, Max 是一个宏)。往该数组添加计划呼叫标志的条件是数组Cj_Work[i].state = 0 (0<= I < Max)。客户端每次向服务端发起执行呼叫任务请求,服务端必须从该客户端呼叫队列提取呼叫任务给他,并帮他呼通通话的另一方。由于该操作是一个线程。所以服务端检测某个客户端的呼叫任务是采用如下的方式:
 ……
 for(int j = 0;j < Max; j++)
 {
      if(Cj_Work[j].state != 0)
    {
            Do some jobs…….;
            Cj_Work[j].state = 0
            Break;
    }
 }
……(1)
 开始服务端向Cj_Work[Max]添加任务采用如下简单的策略:                                                                                                     
 for (int i = 0; i < Max; i++)
 {
        If(Cj_Work[i].state == 0)
     {
              Add work To queue …….;
        Break;
        }
 
}
 
 
…(2)
 
通过阅读代码段 (1)、(2),很容易我们就可以发现这样的任务。在(1)很有可能先处理后添加进来的任务(代码(1)、(2)属于不同的线程),更糟糕的是比较先添加进来的任务可能一直没有被处理。                                                      
 
所以必须修改代码(1)、(2),使其按照时间顺序执行任务。我不想利用Cj_Work[i]中的一个time成员,因为利用这个成员,代码(1)每次检测的时候都要对Cj_Work进行排序,如果使用最快的算法也要Max*log(Max)。
 因此我对每个Cj_Work,添加了一个Cj_Nextpoll变量(初始为0),对代码(1)、(2)做了如下的修改
 
Int k
For(int j = 0;j < Max; j++)
 {
    K = (Cj_Nextpoll+ j )% Max;
      If(Cj_Work[k].state != 0)
    {
            Do some jobs…….;
           Cj_Work[k].state = 0;
           Cj_Nextpoll= (k +1) % Max;
            Break;
    }
 }
 …… (3)
 
 Int k;
 for (int i = 0; i < Max; i++)
 {
    k = (Cj_Nextpoll+ j) % Max;
    If (Cj_Work [k] .state == 0)
     {
              Add work To queue …….;
        Break;
        }
 
}
 
 
… (4)
通过添加了几个变量,我们可以省去了排序(排序面临着变量的拷贝)。 但是必须验证这样的修改是否符合要求(一个简单的方法,可以写个测试程序),但是我想通过证明方式来验证。
 因此我把上述的验证用一个数学模型来描述:
 对于任务J1,….,Jn。按时间先后顺序有Ji<Jk,如果Ji 早于Jk。证明不存在后来的任务先于某个前面的任务被处理。
对于(3),(4),很明显我们可以得到对于Cj_Lastpoll 有:
(*) Jn > JCj_Nextpoll,Jn 后于 JCj_Nextpoll 被处理(JCj_Nextpoll是Cj_Nextpoll对应的时间序任务)。
 
利用这个规则,我们可以利用反正法来证明。假如存在这样的Ji<Jk,Ji后于Jk 被处理。
由(3),我们可以知道存在JCj_Nextpoll = Ji,这样再利用(*)矛盾。
 所以我们只要保证(*)永远成立就可以。
现在我们在来分析:
对于代码(3)
(1)对于任意Ji,存在JCj_Nextpoll = Ji。
(2)对于数组下标k, k = (Cj_Nextpoll + i)%Max,如果i> 0,有Jk 后于JCj_Nextpoll被处理。Jk 为数组k下标对应的时间序任务。
对于代码(4)
(3)对于任意的Jk,Jk > JCj_Nextpoll,有
     k = (Cj_Nextpoll + i)%Max,I > 0。
综合(1)、(2)、(3) 可以得知(*)永远成立。也就证明代码(3)、(4)符合我们的要求。
  也许读者看了这篇文章,会认为我纯粹是把简单问题复杂化,但是我的初衷是像寻求另一种验证程序可靠性的方法,使我们可以付出比较少的代价。本文仅仅是一个引子,欢迎大家探讨!
  以后,我会在工作过程中,继续总结类似的文章,跟大家共享。