轮询锁是实现临界区的正确方法吗?

3
如果我有一个关键部分,我必须实现一种锁定它的方法。我看到了以下变体:
while(lock)
{
    //do nothing
}
lock = true;
// code of critical section
lock = false;

然而,我对此持怀疑态度,因为理论上,多个线程可以执行while(lock)(检查并查看它是否等于false),并一起进入临界区,因为while(lock)lock = true;不是在一个连续的块中执行。 我错了吗?还是这确实是一种不安全的方法?


你需要一个锁,所以使用锁。 - David Heffernan
3个回答

5

你说得没错,这不安全。除此之外就没有更多的话可说了。

编辑:不,确实没有更多关于这个构造的话可说了。这不是自旋锁,也不像自旋锁。要使用自旋锁,需要类似以下的东西:

// note: incomplete, not reentrant, not intended for real use
atomic_type spin_lock = 0;

// enter the spin lock:
int prev_value;

while ((prev_value = test_and_set(&spin_lock, 1)) != 0 || spin_lock != 1)
    ;
// code of critical section

// release the spin lock:
test_and_set(&spin_lock, 0);

重要的一点在于,为了进入自旋锁,你需要以原子方式获取之前的值并设置新值。然后你必须验证你对锁的写操作将其从“未拥有”状态更改为“已拥有”状态。

你说得没错,但只涉及到第一个句子,还有很多需要讲述的。 - Jens Gustedt
@JensGustedt:关于线程安全编程等方面,当然还有很多要说的,但是关于这个特定结构,就没有更多可说的了。 - Jerry Coffin

3
这是“几乎安全”的,也就是根本不安全。它所缺少的正是你所看到的 - 多个线程可能会看到 lock == false 并进入临界区。它需要一个原子操作,必须由硬件支持 - 一种保证只有一个执行线程可以获取锁的方法。
话虽如此,如果你编写的系统可以经受住互斥失败,并且能够在通常情况下正常工作,偶尔出现故障(例如日志记录或其他东西,偶尔的错误条目不一定会导致完全故障),那么这种模式可能“有点”可行...

它需要一个原子操作,这必须由硬件支持。这应该是汇编代码中的一条指令,或者有现成的机制可以实现吗? - Fy Zn
1
这是CPU指令集中的一条指令(或其中之一)。它可以通过原始汇编代码访问,或者如果您的编译器支持此类结构,则可以通过内联汇编代码访问。更好的方法是利用已经编写了正确例程的线程/并发库,但您没有提到这是带有gcc的Linux还是带有VC ++的Windows或其他什么系统... - twalberg
好的,谢谢,我会深入研究的。我正在使用Embarcadero RAD Studio C++ Builder 2010。 - Fy Zn

0
你正在尝试实现的是所谓的自旋锁。新的C标准,C11,实现了一种原始数据类型叫做atomic_flag,可以像这样使用。几乎所有现代硬件都支持它,但不幸的是大多数编译器还没有完全支持它的语法层面,而是有自己的扩展。例如,gcc有内置函数__sync_lock_test_and_set__sync_lock_release可用于此。

这不是自旋锁,甚至也不太像自旋锁。 - Jerry Coffin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接