何时使用信号灯锁定/解锁与等待/通知?

时间:2018-11-28 03:33:44

标签: concurrency promela spin

我正在学习Promela,并使用SPIN对我发现的一些示例进行建模。该模型涉及食物订购模拟。因此,客户订单,收银员接受订单,发送到服务器,再返回给客户等。

Here is a flow of the program.

The specific processes are as followed.

这是我到目前为止编写的代码:

#define NCUSTS 3    /* number of customers */
#define NCASHIERS 1 /* number of cashiers */
#define NSERVERS 1  /* number of servers */
#define NOBODY 255

#define semaphore byte   /* define a sempahore */

/*
 * lock (down) and unlock (up) functions for mutex semaphores
 */
inline unlock(s) {s++;}
inline lock(s) {atomic{ s>0 ; s--}}

/*
 * wait (down) and notify (up) functions for signaling semaphores
 */
inline notify(s) {s++;}
inline wait(s) {atomic{ s>0 ; s--}}

mtype = {CHILI, SANDWICH, PIZZA, NULL} ; // the types of foods (added null for resets)
mtype favorites[NCUSTS];
mtype orders[NCUSTS] = NULL;

byte ordering = NOBODY;

semaphore waitingFood[NCUSTS] = 1;
semaphore cashierOpen = 1;
semaphore serverOpen = 1;

bool waiting[NCUSTS] = false;


/*
 * Process representing a customer.
 * Takes in their favorite food and an integer id
 * to represent them
 */
proctype Customer(mtype favorite; byte id)
{
    /* customer cycle */
    do
    ::

        //Enter
        printf("Customer %d Entered\n", id);

        //Record
        favorites[id] = favorite;

        //Wait for cashier
        cashierOpen > 0;
        atomic{
            lock(cashierOpen);
            printf("Cashier selects customer %d\n", id);
            ordering = id;
        }
        //Order
        orders[id] = favorite;
        printf("Customer orders %e\n", favorite);
        unlock(cashierOpen);
        ordering = NOBODY;


        printf("Customer %d is waiting for %e\n", id, favorite);
        waiting[id] = true;
        wait(waitingFood[id]);
        waitingFood[id] > 0;

        printf("Customer %d recieves food and leaves\n", id);
        favorites[id] = NULL;
        orders[id] = NULL;

    od ;
}

/*
 * Process representing a cashier
 */
proctype Cashier()
{
    do
    ::
        printf("Cashier is waiting for a customer\n");
        cashierOpen < 1;
        printf("Cashier takes the order of Customer %d\n", ordering);
        serverOpen > 0;
        printf("Cashier passes order to server\n");
    od ;
}

/*
 * Process representing a server 
 */
proctype Server()
{

    byte id;
    do
    ::
        printf("Server is waiting for order\n");
        for(id : 0..2){
            if
            ::  waiting[id] ->
                lock(serverOpen);
                printf("Server creates order of %e for %d\n", orders[id], id);
                printf("Server delivers order of %e to %d\n", orders[id], id);
                notify(waitingFood[id]);
                unlock(serverOpen);
            ::  else ->
                    skip;
            fi;
        }
    od ;

}

/*
 * Sets up processes. This model creates two
 * customers with the favorite foods PIZZA & CHILI.
 */
init{

    atomic{
        run Customer(PIZZA, 0) ;
        run Customer(CHILI, 1) ;
        run Cashier();
        run Server();       
    }
}

很显然,该程序无法正常运行。有人可以帮助我了解如何使用信号量以及何时使用锁解锁并在此处通知吗?

1 个答案:

答案 0 :(得分:0)

必须更改模型的这一部分:

::  waiting[id] ->
    ...
    notify(waitingFood[id]);
    ...

waitingFood[id]释放Server时,Customer不会立即将waiting[id]变成false,因此{{1 }}处理同一个Server的请求不止一次(实际上,很可能会发生)。

实际上,通过向模型添加以下 ltl 属性:

Customer

然后检查属性,确认可以为变量ltl p0 { [] (waitingFood[0] < 2) }; 分配“错误”值:

waitingFood

有问题的跟踪是:

~$ spin -search -bfs t.pml
ltl p0: [] ((waitingFood[0]<2))
Depth=      10 States=       13 Transitions=       13 Memory=   128.195 
Depth=      20 States=      620 Transitions=      878 Memory=   128.195 
pan:1: assertion violated  !( !((waitingFood[0]<2))) (at depth 22)
pan: wrote t.pml.trail

(Spin Version 6.4.8 -- 2 March 2018)
Warning: Search not completed
    + Breadth-First Search
    + Partial Order Reduction

Full statespace search for:
    never claim             + (p0)
    assertion violations    + (if within scope of claim)
    cycle checks        - (disabled by -DSAFETY)
    invalid end states  - (disabled by never claim)

State-vector 60 byte, depth reached 22, errors: 1
     1242 states, stored
        1239 nominal states (stored-atomic)
      684 states, matched
     1926 transitions (= stored+matched)
        3 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.104   equivalent memory usage for states (stored*(State-vector + overhead))
    0.381   actual memory usage for states
  128.000   memory used for hash table (-w24)
  128.293   total actual memory usage

pan: elapsed time 0 seconds

根据阅读模型,这里还有一些其他评论:

  • 对于~$ spin -p -g -l -t t.pml ltl p0: [] ((waitingFood[0]<2)) starting claim 4 using statement merging Starting Customer with pid 2 1: proc 0 (:init::1) t.pml:124 (state 1) [(run Customer(PIZZA,0))] Starting Customer with pid 3 2: proc 0 (:init::1) t.pml:125 (state 2) [(run Customer(CHILI,1))] Starting Cashier with pid 4 3: proc 0 (:init::1) t.pml:126 (state 3) [(run Cashier())] Starting Server with pid 5 4: proc 0 (:init::1) t.pml:127 (state 4) [(run Server())] Server is waiting for order 5: proc 4 (Server:1) t.pml:100 (state 1) [printf('Server is waiting for order\\n')] 5: proc 4 (Server:1) t.pml:101 (state 2) [id = 0] Server(4):id = 0 6: proc 4 (Server:1) t.pml:101 (state 3) [((id<=2))] Cashier is waiting for a customer 7: proc 3 (Cashier:1) t.pml:83 (state 1) [printf('Cashier is waiting for a customer\\n')] Customer 1 Entered 8: proc 2 (Customer:1) t.pml:45 (state 1) [printf('Customer %d Entered\\n',id)] Customer 0 Entered 9: proc 1 (Customer:1) t.pml:45 (state 1) [printf('Customer %d Entered\\n',id)] 10: proc 1 (Customer:1) t.pml:48 (state 2) [favorites[id] = favorite] favorites[0] = PIZZA favorites[1] = 0 favorites[2] = 0 11: proc 1 (Customer:1) t.pml:51 (state 3) [((cashierOpen>0))] 12: proc 1 (Customer:1) t.pml:12 (state 4) [((cashierOpen>0))] 12: proc 1 (Customer:1) t.pml:12 (state 5) [cashierOpen = (cashierOpen-1)] cashierOpen = 0 Cashier selects customer 0 12: proc 1 (Customer:1) t.pml:54 (state 8) [printf('Cashier selects customer %d\\n',id)] cashierOpen = 0 12: proc 1 (Customer:1) t.pml:55 (state 9) [ordering = id] ordering = 0 cashierOpen = 0 13: proc 1 (Customer:1) t.pml:58 (state 11) [orders[id] = favorite] orders[0] = PIZZA orders[1] = NULL orders[2] = NULL Customer orders PIZZA 14: proc 1 (Customer:1) t.pml:59 (state 12) [printf('Customer orders %e\\n',favorite)] 15: proc 1 (Customer:1) t.pml:11 (state 13) [cashierOpen = (cashierOpen+1)] cashierOpen = 1 16: proc 1 (Customer:1) t.pml:61 (state 15) [ordering = 255] ordering = 255 Customer 0 is waiting for PIZZA 17: proc 1 (Customer:1) t.pml:64 (state 16) [printf('Customer %d is waiting for %e\\n',id,favorite)] 18: proc 1 (Customer:1) t.pml:65 (state 17) [waiting[id] = 1] waiting[0] = 1 waiting[1] = 0 waiting[2] = 0 19: proc 4 (Server:1) t.pml:103 (state 4) [(waiting[id])] 20: proc 4 (Server:1) t.pml:12 (state 5) [((serverOpen>0))] 20: proc 4 (Server:1) t.pml:12 (state 6) [serverOpen = (serverOpen-1)] serverOpen = 0 Server creates order of PIZZA for 0 21: proc 4 (Server:1) t.pml:105 (state 9) [printf('Server creates order of %e for %d\\n',orders[id],id)] Server delivers order of PIZZA to 0 22: proc 4 (Server:1) t.pml:106 (state 10) [printf('Server delivers order of %e to %d\\n',orders[id],id)] 23: proc 4 (Server:1) t.pml:17 (state 11) [waitingFood[id] = (waitingFood[id]+1)] waitingFood[0] = 2 waitingFood[1] = 1 waitingFood[2] = 1 spin: trail ends after 23 steps #processes: 5 favorites[0] = PIZZA favorites[1] = 0 favorites[2] = 0 orders[0] = PIZZA orders[1] = NULL orders[2] = NULL ordering = 255 waitingFood[0] = 2 waitingFood[1] = 1 waitingFood[2] = 1 cashierOpen = 1 serverOpen = 0 waiting[0] = 1 waiting[1] = 0 waiting[2] = 0 23: proc 4 (Server:1) t.pml:11 (state 14) 23: proc 3 (Cashier:1) t.pml:84 (state 2) 23: proc 2 (Customer:1) t.pml:48 (state 2) 23: proc 1 (Customer:1) t.pml:18 (state 21) 23: proc 0 (:init::1) t.pml:129 (state 6) <valid end state> 23: proc - (p0:1) _spin_nvr.tmp:2 (state 6) 5 processes created ,等待Customer毫无意义,因为它已经在cashierOpen > 0内完成

  • 只有一个lock(cashierOpen);变量的事实意味着,一旦ordering初始化为值cashierOpen

    ,您的模型可能会显示错误的信息/ li>
  • > 1应使用Customer释放cashierOpen,并在unlock(cashierOpen)语句中将ordering设置为NOBODY。否则,其他atomic { }可能会在两条指令之间的Customer中写入内容,然后前一个ordering会错误地用Customer覆盖该变量。

  • 数组NOBODY被初始化为waitingFood[NCUSTS]。我不清楚您在编写1时会发生什么,因为存储位置应该已经包含wait(waitingFood[id]),因此客户不必等待。相反,我认为应该将数组初始化为1,也许还值得更新其名称以反映此更改。

  • 再次在0之后写入waitingFood[id] > 0似乎不仅没有意义,而且在这种情况下也是错误的。在此阶段,信号量应包含wait(waitingFood[id])值!当0/1能够获取信号量时,存储位置wait(waitingFood[id])被设置为waitingFood[id],因此行0将永远阻塞waitingFood[id] > 0。现在不会发生这种情况的唯一原因是由于我在此答案开头加了下划线 bug ,该错误使Customer可以多次投放相同的Server

相关问题