简介:CPN(着色Petri网)新手入门指导是一份面向系统建模入门者的简明教程,覆盖CPN Tools工具的使用,帮助零基础读者从概念到实操掌握CPN建模语言。文档从Places、Transitions、Arcs等基础组件讲起,依次阐述网结构与声明、变迁的使能与发生、并发与冲突、模块化建模、时间建模等核心内容,并延伸到CPN模型的构建、模拟、状态空间分析及性能分析,为验证并发系统行为与优化系统性能提供清晰路径。资源仅包含1个doc文档,压缩包大小约1.3MB,文档体量适中,目录层级分明,可按章节逐步学习,适合计算机科学、通信网络、制造系统、生物信息学等领域的学生和工程师作为首份CPN学习材料。已有1609人学习下载,对于需要快速上手CPN并完成系统行为建模与验证的新手,具有较高的实用参考价值。
1. 别被“着色”俩字吓住:先搞懂 CPN 到底是什么
第一次听说“着色 Petri 网(CPN)”的人,心里多半会冒出两个疑问:它跟画画有什么关系?它又是用来干什么的?我可以直接告诉你:CPN 和美术没有任何关系,它是一套用来描述“系统行为”的建模语言,特别擅长处理那些并发、交互、还有同步问题很突出的系统。你把它想象成一张会动的流程图——普通流程图只能告诉你“步骤长什么样”,而 CPN 这张图能让你在上面放数据、定义规则,然后直接跑起来,看系统在不同情况下到底怎么走、会不会卡住、哪里可能出错。更妙的是,它把图形化表达和标准编程语言(Standard ML)结合在了一起,意思是:你画出来的模型既直观,又能把它当作真正的程序去执行和分析。对刚接触并发系统建模的新手来说,CPN 是一座非常友好的入门桥梁,你不用一上来就啃晦涩的数学定义,只要先理解它那几个核心概念,就足以动手搭建自己的第一个模型。
2. 新手先记四个词:库所、变迁、令牌、颜色集
2.1 库所(Place):系统的“状态”放在哪儿
库所就是 CPN 里那个画成圆圈的东西,它的作用很直白——存放系统中的“状态”。在大多数建模工具里,你要先回答一个问题:这个系统当前处于什么阶段?手头有哪些资源?哪些东西在排队?这些信息都可以放进库所里。
每个库所里可以放若干个令牌(Token),令牌不是光秃秃的记号,它自带一个数据值,这才关键。举个例子,你在给一个发送者和接收者之间的通信协议建模,那么“发送者准备发送第几个数据包”这个状态,就适合放进一个叫 NextSend 的库所里,里面放一个值为 1 的令牌,表示“下一次该发 1 号包了”。另一个库所 PacketsToSend 则放着所有还没发出去的数据包,每个包都是一个令牌,值可能是一个元组,比如 (1, "COL") 就代表“序号为 1、内容是 COL 的数据包”。
从这个例子能看出,库所本身不关心具体数据规则,它只负责保管当前状态。你把令牌放进哪个库所、放多少、带的什么值,共同构成了系统在某一时刻的“标记(Marking)”。每个库所下方通常会写它的颜色集,也就是规定这个库所允许放什么类型的数据值。CPN 里经常见到的颜色集定义长这样:
colset NO = int; colset DATA = string; colset NOxDATA = product NO * DATA;这里做了三件事:第一行把 NO 定义为整数类型,用来表示序列号;第二行把 DATA 定义为字符串类型,用来表示数据包里的内容;第三行把 NOxDATA 定义为前两者的组合类型,也就是一个包含“整数和字符串”的元组。你在模型里给每个库所指定好颜色集,就好像给每个仓库贴上了标签:这个仓库只能放某种规格的货物。这样一来,建模时就不容易出现“放错东西”的低级错误。
有了库所和令牌,你就能描述系统“现在在哪里、手里有什么”。但系统光有状态不够,它还得能发生变化,这就轮到变迁上场了。
2.2 变迁(Transition):系统是如何“动起来”的
如果库所是系统的“静态快照”,变迁就是让系统向前推进的“引擎”。在 CPN 图形里,变迁是一个矩形方块,代表系统中可能发生的一个事件。当这个事件真的发生时,它会从输入库所取走一些令牌,再往输出库所放入一些令牌,这一进一出,系统的状态就发生了改变。
还是刚才那个通信协议的例子:有一个叫 SendPacket 的变迁,它的输入库所是 PacketsToSend 和 NextSend,输出库所是 A(表示数据包进入了网络)。当 SendPacket 发生时,它会从 PacketsToSend 里取走一个数据包,从 NextSend 里拿走当前的序列号,然后把同样的信息放到输出库所 A 里,表示“这个包已经发出去了,正在网络中传输”。
这里有一个值得注意的设计:CPN 允许双箭头弧,也就是同一个库所既当输入又当输出。用双箭头弧连接时,变迁发生会先把令牌从库里取走,又立刻按弧表达式把令牌放回去。所以 SendPacket 虽然从 PacketsToSend 取走数据包,但因为双箭头弧的存在,它同时又会把同样的令牌放回原处。这样做有什么意义?它模拟的是“数据包可以被重复发送”的机制:即使网络丢了包,发送者手里始终还留着备份,可以随时重发。
看到这儿你可能会想:这跟普通编程里的状态变量不是差不多吗?确实有相似之处,但 CPN 的优势在于它的图形化表达让你一眼就能看清整个系统的结构,不用去翻代码才知道哪个变量在哪儿被修改了。而且 CPN 有明确的“并发”语义——两个变迁如果互不影响,就可以同时发生,这是传统命令式编程很难直观表达的地方。
2.3 颜色集与变量声明:让模型携带真实的数据
CPN 的“颜色”并不是真的指红黄蓝绿,它指的是令牌所携带的数据值。为什么要叫颜色?早期的 Petri 网中,所有库所里的令牌都是一模一样的黑点,没有任何区分,建模时很难表达“不同类型的资源”。CPN 给每个令牌附加了一个数据值,这些数据值的可能类型就叫颜色集。说白了,颜色集就是“数据类型”,只是沿用了 Petri 网领域里的传统叫法。
颜色集的定义一看就懂:
colset BOOL = bool; var n : NO; var d : DATA; var success : BOOL;这里值得解释的是变量声明的作用。你在写弧表达式时,需要用到变量来代表“某个满足条件的值”。比如,连接 SendPacket 的弧表达式写成(n, d),其中变量 n 的类型是 NO(整数),变量 d 的类型是 DATA(字符串)。当这个变迁要发生时,系统会去寻找一组绑定(Binding),也就是给这些变量赋予具体的值,比如把 n 绑定成 1、把 d 绑定成 "COL",然后检查按照这组绑定算出来的弧表达式值,是否真的能在对应库所中找到匹配的令牌。如果找不到,这组绑定就不成立,变迁也就不能发生。这部分内容很核心,但不用怕,下一小节就用具体的例子拆开讲。
2.4 使能与发生:为什么有的变迁能动,有的不能
新手最常卡住的概念就是“使能(Enabled)”。一个变迁不是想发生就能发生的,它必须满足两个条件:第一,从输入库所出来的每条输入弧上的表达式,在当前标记下都能取到对应的令牌;第二,如果这个变迁带有守卫(Guard),比如一个布尔条件,那这组绑定还必须让条件成立。
回到那个简单的协议模型。初始状态下,发送方准备发送第一个数据包,所以 NextSend 里有一个值为 1 的令牌,PacketsToSend 里有六个数据包令牌。SendPacket 的输入弧是n和(n, d)。系统会先看n这个变量,因为 NextSend 里只有值为 1 的令牌,所以 n 只能绑定成 1;再看(n, d),在 n 已经等于 1 的情况下,PacketsToSend 里正好有一个令牌 (1, "COL"),于是 d 就绑定成了 "COL"。
于是系统得到了唯一一组使能绑定:
<n = 1, d = "COL">当 SendPacket 在这组绑定下发生时,它从 NextSend 取走 1、从 PacketsToSend 取走 (1, "COL"),同时因为双箭头弧,又把同样的值分别放回去,最后向输出库所 A 放入一个 (1, "COL")。这一步跑完之后,系统的标记就更新了,表现成界面上令牌的位置和数值都发生了变化。
理解这个过程的重点在于:CPN 不是随便把变量赋个值就行,它必须在“当前库所里真实存在的令牌”中找匹配。如果一个输入库所是空的,或者没有一个令牌能跟弧表达式对得上,那这个变迁在这一步就是禁用的。在 CPN Tools 里,使能的变迁会显示成粗边框,你一眼就能看出来下一步有哪些事件可以发生。等你能熟练地判断“哪个库所有什么令牌、哪个变量该绑成什么值”,你对 CPN 的理解就已经迈过最重要的一道坎了。
3. 从零跑通第一个模型:在 CPN Tools 里搭一个能模拟的协议
3.1 装好并认识 CPN Tools 的工作界面
CPN Tools 是目前最常用的 CPN 建模和仿真工具,它是免费的,支持 Windows 和 Linux。第一次打开它,你可能不太适应:它没有传统软件那种菜单栏和下拉菜单,而是把所有操作都放在了工具面板和鼠标右键菜单里。这个设计初看有点怪,但用习惯了会觉得很顺手,因为几乎所有操作都能在画布上直接完成,不需要来回切换窗口。
进入主界面之后,你首先会看到一个空白编辑区,左侧通常有一条工具面板,里面放着创建库所、变迁、弧线等元素的图标。你要做的第一件事,是在画布上试着放一个库所和一个变迁,选中工具栏里的库所图标,在画布上点一下,一个圆圈就出现了;再选变迁图标,在旁边点一下,一个矩形就出现了。接着用弧线工具,从库所拖到变迁,一条带箭头的弧就生成了。整个交互都是鼠标拖动,没有复杂的命令要记。
需要注意,CPN Tools 中所有元素的属性都是通过右键菜单去设置的,比如给库所设置颜色集、给弧表达式填内容,都是选中对象后右键,在弹出菜单里找对应选项。这与大多数软件的属性面板放在右侧不同,算是 CPN Tools 一个鲜明的操作特点。新手阶段不用急着把所有功能都摸清,先学会放节点、连弧线、双击填表达式,就足够开跑了。
3.2 第一步:定义颜色集和变量
打开一个模型文件后,最简单的做法是直接从工具栏里打开声明编辑器。你需要在里面写清楚模型要用到的颜色集和变量。把下面这段代码原样输进去,它可以支撑一个最简单的“发送一个包”的小例子:
colset NO = int; colset DATA = string; colset NOxDATA = product NO * DATA; var n : NO; var d : DATA;这段内容在前面出现过,但这次它是你亲手敲进去的第一段模型代码,所以值得逐行确认。第一行定义了序列号类型,第二行定义了数据内容,第三行定义了“序号加内容”的数据包类型,第四行和第五行声明了两个变量,稍后弧表达式里会用到。声明写完,先点语法检查,确认没有红色报错,再继续下一步。
3.3 第二步:画出三个库所和一个变迁
为了讲清楚发送一个数据包的过程,我们先建一个简化版的发送端。在画布上放置两个库所,分别命名 PacketsToSend 和 NextSend;再放置一个变迁,命名为 SendPacket。用弧线工具把两个库所都连到变迁上,注意默认生成的弧是有方向的,如果发现方向反了,可以右键弧线选择反转方向。
现在要给每个库所设置颜色集。右键点击 PacketsToSend 库所,打开属性面板,把颜色集那一项改成 NOxDATA;右键点击 NextSend,把它改成 NO。这两个库所的初始标记也得写清楚:PacketsToSend 里放三个数据包,NextSend 里放数字 1。
初始标记的写法需要模仿以下格式:
1`(1, "COL") ++ 1`(2, "OUR") ++ 1`(3, "ED ")这里的 1` 表示“一个令牌”,后面括号里是具体的值。两个令牌之间用 ++ 连接,表示把它们合并成一个多集。如果你只放一个包,那写成:
1`(1, "COL")就够了。NextSend 的初始标记则简单得多:
1`1意思是“一个值为 1 的令牌”。这行代码的含义逻辑上很好懂:数字 1 出现了 1 次。
3.4 第三步:填写弧表达式并启动模拟
双击或者右键选择弧线,把两条输入弧的表达式分别填成n和(n, d)。指向 SendPacket 的那条弧来自 NextSend,所以填n;来自 PacketsToSend 的那条弧,填(n, d)。这两条弧决定了变迁发生时从库里取走什么,而取走的规则正是“变量绑定”的匹配过程。
假如你还想让 SendPacket 把一个数据包发到网络侧,可以再加一个库所 A,设颜色集为 NOxDATA,不设初始标记,然后从 SendPacket 拖一条输出弧到 A,弧表达式填(n, d)。这样,当变迁发生,它会把一个数据包从 PacketsToSend 取走,同时复制一份放到 A 里,表示“这个包已经进入网络”。到这里,一个最小模型就算搭好了。
接下来做语法检查。在编辑器的工具栏或者右键菜单里找到语法检查按钮,如果没有任何报错,就可以进入模拟状态。CPN Tools 的模拟模式跟编辑模式是分开的,切到模拟模式之后,你会看到 SendPacket 的边框变成了粗线,这说明在当前标记下它是使能的。用鼠标点击这个变迁,系统会弹出一个对话框,让你选择绑定,通常只有一组可选,也就是<n=1, d="COL">。点确定之后,再观察画布上的令牌变化:PacketsToSend 里的 (1, "COL") 和 NextSend 里的 1 被取走又放回(因为这里如果用的是单箭头就会真正消失),而库所 A 中多了一个 (1, "COL") 令牌。画布上的数字变化非常直观,你立刻能感受到模型“真的在跑”。
4. 深入模拟与状态空间分析:让模型替你回答问题
4.1 交互式模拟:像单步调试一样看每一步
CPN Tools 的交互式模拟是最容易上手、也最适合新手理解的模拟方式,因为它的节奏完全由你掌控。每走一步,系统都会列出当前所有使能的变迁以及它们可用的绑定,你选中哪一个,模型就按哪一组绑定执行一步。这跟你在 IDE 里单步调试程序非常像,只是你调试的对象是一张带数据流动的图。
交互式模拟最大的价值在于理解系统行为。你可以故意选择“数据包丢失”这个绑定,看模型会回到什么状态;也可以选择“成功传输”,看令牌如何一步步走到接收方。这种“人为制造场景”的操作,在调试真实系统时特别有用,因为它让你能精准复现某条执行路径,而不是像自动模拟那样一股脑跑完,出了问题还要回头去猜。
建议刚开始接触 CPN 的读者,先花一点时间用交互式模拟把所有支路都走一遍。你不用急着追求效率,而是要在每一步都问自己一个问题:当前为什么是这些变迁使能?为什么绑定只有这一组?把这两件事想明白了,你基本就掌握了 CPN 的执行机制。
4.2 自动模拟:批量执行看整体表现
模型规模一大,再靠手工一步一步点就不现实了,这时你需要自动模拟。顾名思义,自动模拟就是让系统连续地、自动地执行若干步,直到你事先设置的停止条件被触发为止。它的用途主要有两类:一是快速运行大量步骤来测试模型,看有没有死锁或意外状态;二是配合性能分析,在一次次运行中收集数据,计算出吞吐量、延迟等指标。
CPN Tools 在自动模拟中允许你设置几个关键参数,其中最重要的两个是“最大步数”和“停止条件”。最大步数很好理解,就是模拟最多执行多少步。停止条件则可以是某个库所里令牌数量达到某个值,也可以是某个变迁发生了一定次数。比如你想知道“系统连续运行 100 步之后,接收方到底收到了多少个包”,那你就可以把最大步数设为 100,然后在模拟结束的界面里查看 DataReceived 库所里的令牌数量。这种带停止条件的模拟方式非常灵活,它允许你在不盯着每一步的情况下,把注意力放在系统的最终状态上。
还有一点值得留意,自动模拟默认是随机选择使能变迁的,也就是说,如果系统在某一刻有两个并发事务都可以发生,工具会按某种随机策略选一个。这意味着多次自动模拟的结果可能不一样。这不算是问题,反而是 CPN 仿真的一种特性:它反映了系统本身的非确定性。你在分析结果时,不要因为两次运行结果不同就觉得模型出错了,而是要思考:这种不确定性是否符合真实系统的行为。
4.3 状态空间分析:找出所有可能的状态
交互式模拟和自动模拟,看来看去都是在“一条执行路径”上打转,它们回答的是“这条路径会怎样”。但如果你的系统很复杂,有几条路径根本没想到过怎么办?这就需要状态空间分析出场了。你可以简单地把状态空间看作一张巨大的有向图:所有可能到达的模型状态都成为一个节点,两个节点之间的弧表示某个变迁的某次发生。
CPN Tools 可以自动构造状态空间,然后生成一份报告,里面包含了系统的一些关键属性,比如有没有死锁、有没有某个状态无法到达、系统最多同时有多少个令牌等。对于新手来说,最常查看的就是“死锁”这一项。一个并发系统最怕的事情之一就是死锁:所有变迁都不能发生,整个系统卡死了。如果在状态空间报告中看到“某状态没有任何输出弧”,就说明系统在那个状态下无法继续运行,这往往意味着你的设计漏掉了一种情况。
状态空间报告里的指标很多,你刚开始不用全懂,先抓三个核心就够了:
| 指标 | 含义 | 新手关注点 |
|---|---|---|
| 状态总数 | 系统所有可能状态的个数 | 状态数越多,系统行为越复杂 |
| 弧总数 | 状态之间的变迁连接总数 | 结合状态数判断是否出现异常集中 |
| 死锁状态 | 没有后继状态的状态 | 若有,必须回去检查模型 |
如果你的模型足够小,状态空间分析几乎是瞬间完成,报告中的每一行都对应一个明确的系统性质。等你能熟练读懂这些报告时,建模就从“画图加仿真”升级成了“验证系统性质”,这会是一个特别大的进步。
4.4 避坑指南:新手最容易踩的四个坑
下面几条是我见过很多初学者反复踩的坑,也包括我自己刚入门时的经历。每条按“现象 → 原因 → 解决”的顺序写,方便快速对照。
现象一:语法检查过,一运行却提示“某个变迁没有使能”。这通常是因为你忘了初始化某个库所,或者初始标记里的值跟弧表达式要求的类型对不上。解决办法是逐个检查库所的颜色集和初始标记,尤其是那些没有写初始标记的库所——它们默认是空的,变量自然找不到匹配的令牌。
现象二:模型能跑,但跑几步之后所有变迁都消失了,也就是“死锁”。常见原因是输出库所的类型不对,或者某个变迁发生后把令牌送到了一个不再被使用的库所,导致后面的变迁永远等不到需要的数据。解决思路是借助状态空间报告找到那个卡住的状态,再看这个状态下各库所的令牌分布,对比你的设计意图,找出逻辑缺口。
现象三:自动模拟跑了很多次,结果却“每次都不一样”。首先确认这是不是预期的非确定性表现;如果不是,很可能是模型里引入了随机性(比如随机选择成功或失败的守卫),而你并没有计划它发生。解决方法是回到模型,看看哪些变量或恒定函数具有随机性质,确认它们是否真的符合系统需求。
现象四:弧表达式写得很复杂,结果绑定一个都对不上。这种情况多半是变量类型写错了,比如该用整数的时候用了字符串,或者某个变量名在声明里根本没定义。解决方法是回到声明编辑器,逐一核对变量名和类型,先简化表达式,确保单个变量能匹配后,再逐步增加复杂度。养成先做语法检查再做模拟的习惯,能省下很多排查时间。
5. 性能分析与可视化:从“能跑”到“用得值”
5.1 为什么需要性能分析:光能跑通还不够
能把模型跑起来,只说明你的系统在逻辑上是通顺的,但这离“好用”还差得很远。举个例子,一个通信协议如果频繁丢包重传,从外面看流程也是完整的——数据包一个个发出去,确认一个个收回来,但它可能每秒钟只能成功传输不到一个包。这种性能问题,在纯逻辑模拟里根本暴露不出来,必须在模型中加入时间概念,再来测量系统的延迟、吞吐量和队列长度。
CPN 之所以能支持性能分析,核心在于它拥有“时间建模”能力:你可以给每个变迁指定一个发生延时,表明一个事件从“开始发生”到“完成”需要多长时间。在这个基础上,模型中的时间会不断推进,系统会自动统计每个令牌携带的时间戳,从而计算出各种性能指标。学过操作系统或者计算机网络的人,应该很容易把这种时间模型对应到真实的调度和排队问题里。
5.2 如何在模型中引入时间:从 @+ 开始
在 CPN 里,给模型加入时间的方式很直接:在弧表达式或初始标记中,用 @+ 符号附加一个延时值。这个符号的意思是“当前时间加上多少时间单位之后才真正完成”。
举个例子,假设你想让 SendPacket 这个变迁发送一个数据包需要 5 个时间单位,你就需要在它的输出弧上写:
(n, d) @+ 5这句话的意思是:变迁发生会把 (n, d) 这个令牌送到后继库所,但这个令牌要等 5 个时间单位之后才真正出现在库所中。于是,下一个变迁即使已经使能,也要等到时间轴走到那个时刻才能开始。
如果要模拟更复杂的概率分布,比如网络传输有时快有时慢,你可以引入随机函数。比如这样写:
(n, d) @+ round(exponential(10.0))这里 exponential(10.0) 是 CPN Tools 提供的标准函数,表示一个均值为 10 的指数分布随机数,round 负责取整。用上这类随机延时之后,模型的性能分析就变得更贴近真实情况了。
需要注意,使用了时间延时之后,模型中的令牌会出现“现在可用”和“未来可用”的区别。你在交互式模拟里可能会看到一个变迁迟迟不使能,因为它需要的令牌还没到时间。这是正常的,它其实是在模拟真实系统中“任务正在处理中”的状态。
5.3 数据收集与统计输出:量化你的系统表现
只有时间建模还不够,要衡量系统表现,你还需要“计数器”和“数据收集器”。CPN Tools 允许你在模型的顶层定义一些全局变量,在变迁发生时更新这些变量的值,然后在模拟结束后查看它们的统计结果。
比较常见的做法是定义两个整数变量,一个统计“成功接收的数据包总数”,一个统计“发送过的数据包总数”。每当 ReceivePacket 成功接收到一个数据包,就在发生动作里给接收计数器加一。模拟结束后,用输出表达式把计数器值打印出来,再结合模拟总时长,就能算出吞吐量。吞吐量的公式很简单,就是“成功接收的包数除以模拟时间”,单位可以是“包/时间单位”。你在报告里看到这类数值时,就能直观地比较不同参数配置下系统的性能差异。
性能分析还有一个非常实用的场景,就是“对比不同参数配置”。比如网络的丢包率分别设置为 0.1、0.2、0.3 时,系统的平均吞吐量分别是多少?你不用改模型结构,只需要把守卫条件里那个临界值换成变量,然后跑多组模拟。每一组跑完记录一个结果,最后拉一个简单的表格,性能走向就一目了然了。
5.4 消息序列图与交互图形:把结果讲给不懂模型的人听
模型建得再精巧,如果只有你自己看得懂,价值就打了折扣。CPN Tools 提供了消息序列图(Message Sequence Chart)和交互图形的功能,它能把模型执行过程中的关键事件,按时间顺序展示成一条条消息和状态变化的序列。这在向同事、导师或客户解释系统行为时特别好用。
消息序列图的用法并不复杂:你先在模型里指定哪些事件需要在图中显示,然后运行模拟,CPN Tools 自动把执行过程绘制成一张时序图。你可以看到消息从发送方发出、经过网络、到达接收方,每一步都在时间轴上对齐。这对展示通信协议类的模型几乎是量身定做的功能。交互图形则可以让你在模型上叠加自定义的图形元素,比如用红绿颜色表示某个库所的空满状态,或者用图标代表某种资源,让非专业人员也能轻松理解模型当前的含义。
新手阶段,可视化功能不是必须掌握的,但它属于“越早接触越好”的内容。因为当你需要向别人解释自己的模型时,拿出一张消息序列图所起到的沟通效果,远胜于让对方去读一屏幕的代码和网结构。
6. 入门之后,下一步该学什么
如果你已经跟着前文的步骤,亲手搭出了那个最小模型,并且成功看到令牌在库所之间移动,那么恭喜你,CPN 的大门已经向你敞开了一条缝。接下来的学习路径,我建议按照“加深表达力、提升验证能力、走向真实项目”这样三个阶段推进。
第一阶段要补的知识是层次模型(也叫分层模型)。目前我们所有内容都放在一个平面上,库所和变迁直接相连,模型稍微复杂一点,图面就会变得又乱又挤。层次模型允许你把一个大的变迁拆分成一个子模块,子模块里再继续细化。这样一层一层往下分解,每个层次都保持清晰,最终组合起来依然是一个整体。学习这部分内容时,重点掌握“替代变迁”和“端口库所”这两个概念,前者表示“这个变迁的内部逻辑看子页面”,后者表示“子页面和父页面之间的接口”。
第二阶段建议学习状态空间查询函数。前文只讲了生成报告和看报告,但 CPN 还支持你用查询函数直接检查某条具体性质,比如是否存在一个标记满足某个条件。这需要你稍微了解一点 Standard ML 语法,但你不需要成为这门语言的专家,只要学会写几个简单的查询表达式就够了。查询函数的价值在于,它能把你对系统的疑问变成一句可执行的代码,结果要么是真要么是假,非常明确。
第三阶段就可以尝试一个小项目了。你可以挑一个自己熟悉的应用场景,比如电梯调度、停车场管理、或者一个简单的网络协议,把它用 CPN 建成模型。这期间你会遇到各种之前没碰到的问题,比如模型太大怎么办、怎么拆成模块、性能数据怎么收集分析。回到这篇教程里提到的关键点,逐一对照排查,大多数坑都能自己迈过去。
我个人有一条坚持到现在的习惯:每建一个模型,都强制自己按顺序走一遍“声明检查、语法检查、交互模拟、自动模拟、状态空间分析”这五步,就算再小的模型也不例外。这么做看起来多花了一点时间,但能把很多低级错误挡在早期,少踩很多坑。希望这篇入门指南能帮你顺利跨出第一步,也希望你不要被最初的“着色”两个字吓退——它背后的世界,远比名字看上去有趣。
本文还有配套的精品资源,点击获取