文章目录
并发模型
语法
- 通过双竖线表示两个进程
P, Q
同步进行
ITCH = (scratch -> STOP).
CONVERSE =(think -> talk -> STOP).
||CONVERSE_ITCH = (ITCH || CONVERSE).
数学规则
- 并发模型满足交换律和结合律
- 如果 P, Q 之外还有第三个进程同步进行的话,写成如下形式:
ITCH = (scratch -> STOP).
CONVERSE =(think -> talk -> STOP).
LAUGH = (laugh -> LAUGH).
||CONVERSE_ITCH = (ITCH || LAUGH).
||CONVERSE_ITCH_LAUGH = (CONVERSE_ITCH || LAUGH).
- 将
CONVERSE_ITCH
先并行了之后,再与LAUGH
并行
行人过马路案例
行人随意过马路
- 一个行人可以随时过马路
PEDESTRAIN = (wander -> PEDESTRAIN).
- 红绿灯在 红绿黄三个颜色之间变化
TRAFFIC_LIGHT = (green -> yellow -> red -> TRAFFIC_LIGHT).
- 行人如果不管交通灯随意过马路,那么这个程序可以如此模拟:
||LIGHT_PEDESTRIAN = (PEDESTRAIN || TRAFFIC_LIGHT).
行人按照交通灯过马路
-
如何让行人按照和交通灯的交互来过马路:
- 让两个进程之间共享动作(shared actions)
-
行人在某个时刻会有两种状态:
PEDESTRAIN = (button -> PEDESTRAIN
| wander -> PEDESTRAIN).
- 交通灯会有下面几种状态:
TRAFFIC_LIGHT = (button -> YELLOW | idle -> GREEN),
GREEN = (green -> TRAFFIC_LIGHT),
YELLOW = (yellow -> RED),
RED = (red -> TRAFFIC_LIGHT).
- 当让行人和交通灯之间有交互,状态图如下:
- 在这个过程中,我们发现在这个交互的状态图中没有特别标明是哪个进程在操作 button,因为 button 对两个进程来说是共享的
- 我们可以很自然的把 button 看做是 pedestrian 的输出,同时也是 traffic_light 的输入。但是对于这个状态图来说,它就仅仅只是一个共享的 action 仅此而已。
动作重标记(Action relabelling)
- 在上面交通灯——行人的例子中,行人可以选择
button
或者wander
行为;然而,当行人选择了wander
,那么就预示着交通灯的 button 没有被按下;因此wanderring
操作和交通灯的idle(空闲)
操作可以看成是一个操作。 - 在这种情况下我们可以选择更改
TRAFFIC_LIGHT
和PEDESTRIAN
让这个action
有同样的名字,但是这并不是一种理想的方式,因为这种重命名的操作可能会导致另外一个复合进程的break
。 - 因此我们采用一种方式——
action relabelling
。
语法
- 给定一个进程
P
, 我们可以用P/{new1/old1,...,newN, oldN}
来表达和P
同样的意思,只是action old1
被改名为了new1
- 上面的例子中,我们说
idle
和wander
是等同的状态,因此,我们将上面的语句可以写成下面的形式:
TRAFFIC_LIGHT = (button -> YELLOW | idle -> GREEN),
GREEN = (green -> TRAFFIC_LIGHT),
YELLOW = (yellow -> RED),
RED = (red -> TRAFFIC_LIGHT).
PEDESTRIAN = ( button -> PEDESTRIAN
| wander -> PEDESTRIAN
).
||LIGHT_PEDESTRIAN =
(TRAFFIC_LIGHT || PEDESTRIAN/{idle/wander}).
- 前面两个进程中的所有内容都不需要重新编写,只是在两个进程进行并行复合的时候,将
PEDESTRIAN
中的wander
重标记成TRAFFIC_LIGHT
中的idle
- 可以看到,
PEDESTRIAN
中的wander
也被重标记成了idle
客户机—服务器案例
单个客户机和服务器交互
- 定义一个客户机:
CLIENT = (call -> wait -> continue -> CLIENT).
- 定义一个服务器:
SERVER = (request -> service -> reply -> SERVER).
- 同时开启客户机和服务器:
|| CLIENT_SERVER = (CLIENT || SERVER)
- 其实服务器和客户机有同时发生的
action
,比如服务器的request
和 客户机的call
是同时发生的,客户机的wait
和服务器的reply
是同时发生的,因此我们替换着两组action
|| CLIENT_SERVER = (CLIENT || SERVER) /{call/request, reply/wait}.
- 可以发现,经过
action relabelling
可以让整个状态简化不少
多个同类进程进行区分
- 当存在多个客户机,我们自然而然地想写如下的操作语句:
CLIENT = (call -> wait -> continue -> CLIENT).
||TWO_CLIENTS = (CLIENT || CLIENT).
- 然而,由于所有的
action
在不同的CLIENT
进程之间都是共享的,因此,这个操作的结果就等同于CLIENT
本身:
进程标记(Process labelling)
- 当有多个相同类型的进程的时候,如果不对进程进行标记,就会出现上述情况,当我们采取进程标记,我们就可以很好的区分多个类型相同的进程。
语法
|| TWO_P = (a:P || b:P)
- 用在上面的多个
CLIENT
的例子中就是:
CLIENT = (call -> wait -> continue -> CLIENT).
||TWO_CLIENTS = (a:CLIENT || b:CLIENT).
Process labelling
就是通过添加 ”前缀“ 的方式来给相同类型的进程加以区分,这样他们就不会共享action
了
N 个进程标记
- 以下三种写法是完全等同的:
const N = 3
CLIENT = (call -> wait -> continue -> CLIENT).
||N_CLIENTS = (c[i:1..N]:CLIENT).
CLIENT = (call -> wait -> continue -> CLIENT).
||N_CLIENTS(N=3) = (c[i:1..N]:CLIENT).
CLIENT = (call -> wait -> continue -> CLIENT).
||N_CLIENTS(N=3) = (forall[i:1..N] c[i]:CLIENT).
一个 Server 和 多个 client 进程复合
-
回顾一下前面的内容:
- 当我们要让两个进程之间产生关联和交互,我们需要采用
action relabelling
来产生联系 - 当多个同类进程之间进行区分我们通过
process
labelling
加前缀来让同类的process
不能共享。 - 那么当我们在使用一个
server
和 多个同类的client
交互的时候,我们既要区分多个同类client
,还要让他们和server
之间产生关联而对他们的action relabelling
- 但是由于多个
client
已经不共享action
了,难道我们要一个个的把每个client
中的action
手动改掉么?当然不是,我们采用前缀集合的方式(prefix set
)批量修改action
。
- 当我们要让两个进程之间产生关联和交互,我们需要采用
-
例如当只有两个
client
和server
交互,我们如下写:
CLIENT = (call -> wait -> continue -> CLIENT).
SERVER = (request -> service -> reply -> SERVER).
||CLIENT_2 = (a:CLIENT || b:CLIENT || {a,b}::(SERVER/{call/request, wait/reply})).
- 他们复合后的进程状态
- 将这种交互拓展到
N
个同类型的CLIENT
和SERVER
交互的情况:
CLIENT = (call -> wait -> continue -> CLIENT).
SERVER = (request -> service -> reply -> SERVER).
||N_CLIENT_SERVER(N=2)
= (forall [i:1..N] (c[i]:CLIENT)||{c[i:1..N]}::(SERVER/{call/request, wait/reply})).
变量隐藏(Variable hiding)
-
为了减少系统建模时候的复杂度,经常会通过隐藏一些变量来达到目的。
- 给定一个进程
P
,进程P\{a1, ..., aN}
表达的意思和进程P
是完全相同的,但是a1...aN
这些action
会被隐藏起来。 - 能达到隐藏效果的语句还有:
P@{a1,...aN}
;使用@ 来表示那些不被隐藏的 action
,其他的会被隐藏。那些被隐藏起来的状态在图中会用tau
表示 - 因此
@
操作符和\
操作符可以认为是互补的
- 给定一个进程
-
下面的语句效果相同:
SERVER_1 = (request -> service -> reply -> SERVER_1)
@{request , reply}.
SERVER_2 = (request -> service -> reply -> SERVER_2)
\{service}.
- 第一个例子中通过
@
保留了request
和reply
action - 第二个例子则使用
\
来隐藏service