你有几个选择,都围绕在 N 个进程之间实现某种互斥算法:
Black & White Bakery Algorithm的实现可在此处获得。但是请注意,这些算法(可能除了 Peterson 的算法)往往很复杂,并且可能使您的系统验证变得不切实际。
一种比较简单的方法是诉诸测试和设置算法,但它仍然atomic
在尝试部分中使用。这是取自此处的示例实现。
bool lock = false;
int counter = 0;
active [3] proctype mutex()
{
bool tmp = false;
trying:
do
:: atomic {
tmp = lock;
lock = true;
} ->
if
:: tmp;
:: else -> break;
fi;
od;
critical:
printf("Process %d entered critical section.\n", _pid);
counter++;
assert(counter == 1);
counter--;
exit:
lock = false;
printf("Process %d exited critical section.\n", _pid);
goto trying;
}
#define c0 (mutex[0]@critical)
#define c1 (mutex[1]@critical)
#define c2 (mutex[2]@critical)
#define t0 (mutex[0]@trying)
#define t1 (mutex[1]@trying)
#define t2 (mutex[2]@trying)
#define l0 (_last == 0)
#define l1 (_last == 1)
#define l2 (_last == 2)
#define f0 ([] <> l0)
#define f1 ([] <> l1)
#define f2 ([] <> l2)
ltl p1 { [] !(c0 && c1) && !(c0 && c2) && !(c1 && c2)}
ltl p2 { []((t0 || t1 || t2) -> <> (c0 || c1 || c2)) }
ltl p3 {
(f0 -> [](t0 -> <> c0))
&&
(f1 -> [](t1 -> <> c1))
&&
(f2 -> [](t2 -> <> c2))
};
在您的代码中,您应该lock
为每组3
相关线程使用不同的变量。锁争用仍会在全局级别发生,但在临界区中工作的某些进程不会导致其他进程等待,而不是属于同一线程组的进程。
另一个想法是利用通道来实现互斥:让每组线程共享一个公共的异步通道,该通道最初包含一条token
消息。每当这些线程之一想要访问临界区时,它都会从通道中读取。如果token
不在通道内,它会一直等待,直到它变得可用。否则,它可以在临界区继续前进,当它完成时,它会将token
后部放入共享通道中。
proctype node (chan inp; byte ppid)
{
chan lock = [1] of { bool };
lock!true;
run recv_A(lock);
run send_B(lock);
run do_C(lock);
};
proctype recv_A(chan lock)
{
bool token;
do
:: true ->
// non-critical section code
// ...
// acquire lock
lock?token ->
// critical section
// ...
// release lock
lock!token
// non-critical section code
// ...
od;
};
...
这种方法可能是最简单的开始,所以我会先选择这个。但是请注意,我不知道这会如何影响验证期间的性能,这很可能取决于Spin
. 此解决方案的完整代码示例可以在文件中找到channel_mutex.pml
。
最后,请注意,您可能希望在模型中添加互斥、进度和锁定自由 LTL
属性,以确保其行为正确。这些属性的定义示例可在此处获得,代码示例可在此处获得。