无锁和非阻塞有什么区别?

3
在数据结构同步的背景下,有人能澄清“无锁”和“非阻塞”的区别吗?这些术语似乎被很多人交替使用,但我还不确定是否有一些微妙的差别隐藏在其中。
我的意思是,无锁是“没有锁”,而非阻塞更像是保证进展。我怀疑一个暗示另一个,但不是相反的情况,我不确定。
欢迎提供参考。

“非阻塞”最好从避免进度障碍的角度来考虑(甚至可能涉及自旋锁和线程)。每个函数调用都会及时返回或返回部分工作(协同程序yield,promise或在完成后回调)。锁定是一种正确性策略,具有正确性障碍和进度障碍,例如持锁死亡和amdahls法则。另一种非锁定的正确性策略和进度策略调用可交换属性(无锁结构,不变量)可能是合适的。 - Rob
5个回答

6
锁定是一种访问控制机制,意味着当您想要独占地访问资源时,您会锁定该资源。锁定房间,使用房间/做您想做的事情,现在为他人解锁房间,以便他们可以使用它。在房间被锁定时,没有其他人可以进入房间,因此也无法进行任何操作。
阻塞用于保证数据检索,即除非您没有数据,否则不要返回。在门/管道/套接字(基本上任何东西)处等待,当数据可用时获取它并返回。
补充--
不要被单词的字面英语含义所困惑,因为它们都可以在您尝试将它们放入的上下文中互换使用。例如 - 锁定就像阻止其他人使用相同的资源,而阻塞可以将自己(调用函数)锁定到资源,直到数据可用。
因此,锁定仅意味着您捕获资源一段指定的时间(除非您取消阻止它)。 而阻塞是您被阻止了,这意味着您不能继续前进,因为您没有数据可以继续或前进。
它们的实现方式是通过更改进程的状态,并等待中断或事件发生。

如果我的理解是正确的,那么锁定会阻塞吗? - Giovanni Funchal
@Helltone - 通常情况下,只有在其他人正在使用锁时,锁才会阻塞。 - Eric Petroelje
1
这些评论对我来说很困惑。所以你的意思是,“无锁”意味着其他任务的完成不依赖于自己,而“非阻塞”则表示自己的完成不依赖于其他任务。这是你的意思吗? - Giovanni Funchal
@Helltone -- 没有像 lockless 这样的东西。我再次非常清楚地说,不要只按照单词的英文含义去思考,要尝试重新思考。 - Vivek Sharma
有些编程语言允许你尝试获取锁,但如果无法获取,则立即返回以实现非阻塞。如果你可以调用一个函数,并保证它能够在截止日期前不断取得进展或返回结果,那么它就是非阻塞的。 - Rob
显示剩余3条评论

3
他们是完全不同的。
锁定意味着您使用某种方法来控制文件访问,使用锁定可以防止两个进程同时写入同一文件,防止一个进程在另一个进程读取时写入文件,但允许两个进程同时读取。
阻塞意味着该方法将等待操作完成后再返回。
更新
针对示例请求...如果我有时间,我将尝试添加示例,但现在,这里是可能性的解释。
我们有3种执行锁定的方式:
1.无
2.阻塞。如果锁定不可用,则等待它。
3.非阻塞。如果锁定不可用,则失败。
我们有2种执行IO的方式:
1.阻塞。等待缓冲区准备好。
2.非阻塞。如果我们无法立即读取/写入,则失败。
如果我们像平常一样使用open()和read(),我们会得到阻塞IO。如果我们想要非阻塞IO,我们必须向open()传递O_NONBLOCK标志,然后read()将返回E_AGAIN而不是阻塞。
默认情况下没有锁定。我们可以调用fcntl()并使用F_SETLK或F_SETLKW获取锁定。前者在锁定不可用时阻塞,后者以EACCES或EAGAIN失败。
我认为有两个可能引起混淆的点:
1.IO可以是阻塞/非阻塞的,锁定可以是阻塞/非阻塞的。
2.除了数据未准备好之外,IO请求可能会因为另一个进程锁定文件而被阻止。

锁定不仅用于文件访问,但我理解你的答案。我不确定它们是“完全”不同的,也许一个意味着另一个?或者在某些情况下它们是相同的,但并非普遍如此。那么我的问题是:哪些情况? - Giovanni Funchal
当然,锁定适用于各种访问方式 - 文件访问只是一个简单的例子。它们是正交问题,你可以有其中一个而没有另一个。 - Draemon
@Draemon "你可以有其中之一而没有另一个"。这里可以举例说明。 - Giovanni Funchal
还没有例子,但有一个解释。 - Draemon

1

是的,无锁意味着没有锁定机制。非阻塞意味着调用将立即返回,而不是等待某些外部事件(例如释放锁或数据到达缓冲区)发生。可以使用锁并使用非阻塞调用,例如在调用中。

flock(fh, LOCK_SH | LOCK_NB);

这意味着“尝试获取读锁,但如果无法获取,请不要等待,立即返回并告诉我你无法获取”。没有LOCK_NB(“非阻塞”)的LOCK_SH(“共享锁”)的默认行为是等待锁可用。


1

它们可能相似,但通常在不同的上下文中使用。在数据结构的上下文中,它们将是相同的东西。您还可以在IO的上下文中使用“非阻塞”,在这种情况下,它意味着函数在返回之前不会等待操作完成或者该操作肯定不会被阻塞(例如读取已经被缓存的数据)。

此外,“非阻塞”并不意味着某些东西是无锁的。例如,一个数据结构可能使用锁,但具有一些不需要锁的非阻塞操作和其他需要锁的阻塞操作。


0
一个通过示例的初步答案:
考虑一个具有两个方法wait()和notify()的对象(称为“事件”)。
  • 实现方式1:

    notify()原子地设置一个布尔值。 wait()循环直到布尔值为真。两者都是无锁的,但wait()阻塞式的。

  • 实现方式2:

    notify()获取锁,设置布尔值,然后释放锁。 wait()获取锁,读取布尔值,释放锁,所有这些都在循环中进行,直到布尔值为真。因此,两者都是基于锁的,阻塞式的。

  • 实现方式3:

    最初锁被使用。 notify()检查布尔值,如果为真,则释放锁。 wait()获取锁并将布尔值设置为true。 notify()非阻塞式的(是否是无锁的有争议)。wait()基于锁的,阻塞式的。

因此,我会说非阻塞意味着无锁,但它们并不等同,因为无锁操作仍然可以在循环中阻塞条件。


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