今天跟大家唠唠我最近折腾的“皮特森”项目,这名字听起来挺唬人,就是个简单的并发控制实验。
我琢磨着怎么才能让多个线程安全地访问共享资源,又不至于搞得太复杂,影响效率。锁当然是个选择,但锁用多容易死锁,而且性能开销也不小。我就想试试无锁算法。
正看到网上有人提到皮特森算法,说它简单易懂,适合新手入门。于是我就决定拿它开刀。
我找份皮特森算法的伪代码,看起来挺简洁的:每个线程都有一个“想进入临界区”的标志,和一个“谦让”标志。如果两个线程都想进入临界区,那么其中一个必须“谦让”,让另一个线程先进入。
看完伪代码,我就开始撸代码。我用Java写一个简单的程序,模拟两个线程竞争访问一个共享的计数器。每个线程都会循环增加计数器的值,然后打印出来。
在代码里,我定义两个boolean类型的数组:`flag`和`victim`。`flag[i]`表示线程i是否想进入临界区,`victim`表示哪个线程应该谦让。
我把`flag`数组的所有元素都初始化为`false`,表示两个线程都不想进入临界区。
然后,在每个线程的`run`方法里,我把`flag[i]`设置为`true`,表示线程i想进入临界区。我把`victim`设置为另一个线程的ID。
就是一个while循环,循环条件是`flag[1-i] && victim == i`。这个循环会一直阻塞当前线程,直到另一个线程不想进入临界区,或者当前线程被指定为“谦让”的线程。
一旦跳出循环,就表示当前线程可以安全地进入临界区。在临界区里,我增加计数器的值,然后打印出来。
我把`flag[i]`设置为`false`,表示线程i已经离开临界区,可以释放资源。
代码写完,我兴冲冲地跑一下,结果发现有问题!有时候,两个线程会同时进入临界区,导致计数器的值不正确。
我开始debug,仔细检查代码,发现问题出在while循环的条件判断上。当两个线程几乎同时设置`flag`和`victim`的值时,可能会出现两个线程都跳出循环的情况,导致它们同时进入临界区。
为解决这个问题,我调整while循环的条件,增加额外的判断。具体来说,我把原来的`flag[1-i] && victim == i`改成`flag[1-i] && victim == i && flag[i]`。
这样修改后,只有当另一个线程想进入临界区,当前线程被指定为“谦让”的线程,并且当前线程自己也想进入临界区时,才会阻塞。
修改后的代码跑起来就没问题,两个线程可以安全地访问共享计数器,计数器的值也正确。
这回实践,让我对并发控制有更深的理解。皮特森算法虽然简单,但它也揭示并发编程的一些基本原理。而且通过自己动手实现,也让我对无锁算法有更直观的认识。
皮特森算法也有它的局限性。它只适用于两个线程的竞争,而且在某些情况下,性能可能不如锁。但是,对于理解并发编程的基本概念来说,它还是一个不错的入门例子。
这回“皮特森”实践还是挺有收获的,虽然踩一些坑,但也学到不少东西。以后有机会,我还想尝试其他的并发控制算法,看看能不能找到更高效、更可靠的解决方案。
还没有评论,来说两句吧...