本文源于Stanford大学公开课《编程范式》第18讲中讲解的一个通过线程和信号量去模拟一个冰淇淋店的运营情况,我觉得很有意思,所以就着重的摘录实现了一下。
模型的角色分类
维持这个冰淇淋店运营模型的角色分为四类:
- 管理员:负责检验操作员制作的冰淇淋是否合格,并给操作员一反馈,如果检验合格操作员把冰淇淋交给顾客;如果不合格操作员重新制作冰淇淋然后再找管理员检验,直到合格为止。假如:3个顾客没人请求了2个冰淇淋,管理员必须检验合格6个冰淇淋才算完成了自己的目标。假设:管理员只有一位,所以操作员们请求检验时存在竞争关系。
- 操作员:负责接受顾客的请求,然后去制作冰淇淋然后找管理员去检验制作的冰淇淋是否合格,如果合格则将冰淇淋交给顾客;如果不合格则重新制作冰淇淋再次去找管理员检验,直到制作出合格的冰淇淋为止。假设:操作员的数量跟顾客请求的冰淇淋数量相同,在请求检验的时候操作员之间会竞争一个管理员。
- 顾客:通过一个随机函数得到每个顾客需要的冰淇淋的数量,然后分派给几个操作员去制作这些冰淇淋,然后等待这几个操作员制作出合格的冰淇淋后,最后再去收银员那里去付款。假设:顾客的数量是固定的,顾客负责分派任务给操作员,需要几个冰淇淋就要起几个操作员线程去制作冰淇淋,等到这几个操作员给出合格的冰淇淋后,最后顾客拿着冰淇淋去找收银员付款,付款的过程顾客之间需要排队,先找到收银员的先结账,保持先进先出的原则。
- 收银员:负责按照顺序给顾客结账。假设:收银员只有一个,所以顾客在付款的时候需要先竞争到收银员,先来的先结账后来的后结账。
关系图如下:

分析角色竞争关系建立数据结构
通过上面的描述我们知道了管理员与收银员只有一位,操作员会竞争管理员拿到进行检验的资格;顾客需要竞争收银员进行付款,但是顾客之间是先到先付款的关系,所以需要维持一个信号量数组来维持这个队列。
为了维持操作员对管理员的竞争关系,建立如下结构体以及其初始化函数:
1 | // 同步管理员检查的结构体 |
为了维持顾客对收银员的竞争关系,并保证顾客先到先付款的队列关系,建立如下结构体以及其初始化函数:
1 | // 同步收银员收银的结构体 |
为了使得输出信息后台输出更加完善易懂,我们定义了一下结构体,传递给顾客线程:
1 | // 传递给customer线程的参数 |
为了使得信号量调试方便我们对信号量进行了简单的封装:
1 | typedef struct{ |
分析角色职责建立任务线程
- 根据上面描述创建管理员线程:
1 | // 管理员线程 |
- 根据上面描述创建操作员任务线程:
1 | // 操作员线程 |
- 根据以上描述创建顾客任务线程:
1 | // 顾客线程 |
- 根据以上描述创建收银员任务线程:
1 | // 收银员线程 |
- 最后在main函数中我们要启动管理员线程、收银员线程和顾客线程:
1 | int main(int argc, const char * argv[]) |
编译运行输出结果
1 | 第0号顾客请求了1个冰淇淋 |
写在后面:
程序中可能还存在需要完善的地方,比如:我们可以给操作员多传一个参数,明确这是第几号顾客的冰淇淋;还有就是为了控制顾客排队结账而在struct payment
结构体中为每一个顾客声明了一个信号量signal_t customers[...]
,但是在收银员线程中却是用for
循环去依次发信号的;我们还可以在每个角色任务线程中加入的函数(如makeCream()
,walkToCasher()
…)中通过绘图代码去绘制一些图画或动画,这样能够更加直观的展现冰淇淋店的运营情况,这些待完善的部分就交给大家去完成吧。