自旋锁是否属于无锁技术?

8

我对这两个概念有些困惑。

维基百科上锁定自由的定义为:

如果保证整个系统可以进展,则非阻塞算法是无锁定的

非阻塞的定义为:

如果任何线程的失败或暂停不会导致另一个线程的失败或暂停,则称算法为非阻塞

我以为自旋锁是无锁定的,或者至少是非阻塞的。但现在我不确定了。因为根据定义,"自旋锁不是无锁定的" 也对我有意义。例如,如果持有自旋锁的线程被挂起,则会导致在外自旋的其他线程也被挂起。因此,根据定义,自旋锁即使没有锁定,也不是非阻塞的。

我现在很困惑。有人能清楚地解释一下吗?


一个自旋锁如果在自旋时无法回退到锁定状态,通常被称为“bug”。 - Hans Passant
是的,无锁编程是那些不幸的误称之一,它的意思与大多数人想象的完全不同。 - Voo
@Voo:人们最常认为的是什么?无等待?还是避免使用任何名为“锁”的库实现(而他们没有意识到用原子操作自己编写锁仍然是一种锁)? - Peter Cordes
1个回答

11

任何可以称为锁的东西(将其他线程从关键部分中排除,直到当前线程解锁)都符合定义,因此不能被称为无锁。是的,自旋锁是一种锁。

如果一个线程在持有锁的同时进入睡眠状态,那么其他线程就无法获取该锁并取得进展,自旋锁无法防止这种情况。操作系统可以随时取消调度一个线程,即使它正在进行关键部分的操作。


请注意,“无锁”并不等同于“无等待”,因此无锁算法仍然可能具有cmpxchg重试循环等内容,但只要一个线程每次都成功,它就是无锁的。

无等待算法甚至不能有这个,最多只能等待缓存失效/争用原子操作的硬件仲裁。维基百科的无阻塞算法文章更详细地定义了无等待和无锁。


我认为你混淆了“阻塞”的两个定义。

我认为你在谈论一个spin_trylock函数,它尝试获取自旋锁,并在失败时返回错误,而不是自旋等待。因此,在与非阻塞I/O相同的意义下,这是非阻塞的:如果资源不可用,返回错误而不是等待。

这并不意味着系统中的任何线程都在进行受自旋锁保护的进度。这仅仅意味着你的线程可以在尝试获取锁之前做其他事情,而不需要使用单独的线程来并行执行等待获取锁的操作。


在一个无限循环中自旋等待也被认为是阻塞/无法取得进展。按照这个定义,纯自旋锁和通过操作系统辅助休眠等待直到另一个线程解锁的锁之间没有区别。

无锁的定义不涉及浪费CPU时间/电源以腾出空间让独立的工作发生。


有些相关:获取无争用自旋锁不需要系统调用,这意味着它是一种“轻量级”锁。一些锁实现即使在无争用情况下也总是使用(相对较慢的)系统调用。参见Jeff Preshing的Always Use a Lightweight Mutex文章。还要阅读Jeff的其他帖子学习无锁编程,因为它们非常出色。事实上,[lock-free]标签的维基链接到他们。


@Mave:不,除非有其他线程的干扰,否则cmpxchg尝试成功。这与等待内存中特定值的自旋循环非常不同。当N个线程在锁步中尝试cmpxchg时,每次迭代至少有1个线程将成功。一个线程不可能处于一种状态下休眠,使得其他线程无法前进,而活锁也是不可能的。CAS重试循环在正式的CS术语中是“无锁”的,但不是“无等待”的:https://en.wikipedia.org/wiki/Non-blocking_algorithm - Peter Cordes
@Mave:不行。如果N个线程同时尝试使用xchg获取自旋锁,它们都可能失败,没有任何线程可以向前推进。因此,这是一种阻塞算法,不像CAS重试循环,其中您尝试CAS的值是从上一次迭代的CAS读取派生的。(此外,这是一种实现自旋锁的低效方法。自旋只读,直到看到它解锁,这样您就不必让N个线程垃圾邮件原子RMW操作。请参见通过内联汇编锁定内存操作以获得一个天真但不可怕的x86 asm示例。) - Peter Cordes
@Mave:关键是:一个线程能否在进入沉睡或死亡状态时,把内存内容留在某种状态下,使得所有其他线程都会无限制地阻塞? 是的:阻塞算法。 不是:无锁算法。(但不一定是等待自由。) - Peter Cordes

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