如果我有一个关键部分,我必须实现一种锁定它的方法。我看到了以下变种:
while(lock)
{
//do nothing
}
lock = true;
// code of critical section
lock = false;
但是,我对它持怀疑态度,因为从理论上讲,几个线程可以执行while(lock)
(检查并看到它是假的),然后一起进入临界区,因为while(lock)
和{{1不是在一个连续的块中执行。我错了吗?或者这确实是一种不安全的方法?
答案 0 :(得分: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);
重要的一点是,要进入自旋锁,您需要获取先前的值并设置新值 atomically 。然后,您必须验证您的写入锁定是否已将其从“非拥有”更改为“拥有”状态。
答案 1 :(得分:3)
这“几乎是安全的”,即根本不安全。它缺少的正是你所看到的 - 多个线程可以看到lock == false
并进入关键部分。它需要一个必须由硬件支持的原子操作 - 这种方法可以保证只有一个执行线程可以获得锁定。
也就是说,如果你正在编写的系统能够在互斥失败的情况下存活下来,并且通常偶尔会出现故障(可能是日志记录或偶尔出现错误输入并不一定会导致完全失败的事情),那么这个模式可以“sorta”工作......
答案 2 :(得分:0)
您要实现的内容称为自旋锁。新的C标准C11实现了一个名为atomic_flag
的原始数据类型,可以像这样使用。几乎所有的现代硬件都支持它,但不幸的是,大多数编译器还没有完全支持它在语法层面上,但有自己的扩展。例如,gcc为此构建了__sync_lock_test_and_set
和__sync_lock_release
。