自旋锁与信号量

2024-04-06

信号量和自旋锁之间的基本区别是什么?

我们什么时候会使用信号量而不是自旋锁?


自旋锁和信号量主要有四个不同点:

1. 它们是什么
A spinlock是锁的一种可能实现,即通过忙等待(“旋转”)实现的锁。信号量是锁的概括(或者,相反,锁是信号量的特例)。通常,但不一定,自旋锁仅在一个进程内有效,而信号量也可用于在不同进程之间进行同步。

锁的作用是互斥,即one线程一次可以获得锁并继续执行代码的“关键部分”。通常,这意味着修改多个线程共享的某些数据的代码。
A 信号有一个计数器,并允许自己被一个或几个线程,取决于您向其发布的值,以及(在某些实现中)取决于其最大允许值。

到目前为止,我们可以将锁视为最大值为 1 的信号量的一种特殊情况。

2. 他们做什么
如上所述,自旋锁是一种锁,因此是一种互斥(严格1对1)机制。它的工作原理是通常以原子方式重复查询和/或修改内存位置。这意味着获取自旋锁是一项“繁忙”操作,可能会长时间(也许永远!)消耗 CPU 周期,而实际上却“一无所获”。
这种方法的主要动机是上下文切换的开销相当于旋转几百(甚至几千)次,因此如果可以通过燃烧几个周期旋转来获取锁,那么总体上来说这很可能是更高效。此外,对于实时应用程序来说,阻塞并等待调度程序在未来某个遥远的时间返回它们可能是不可接受的。

相比之下,信号量要么根本不旋转,要么只旋转很短的时间(作为避免系统调用开销的优化)。如果无法获取信号量,它将阻塞,将 CPU 时间让给准备运行的其他线程。当然,这可能意味着在再次调度线程之前需要几毫秒的时间,但如果这没有问题(通常不是),那么它可能是一种非常高效、CPU 保守的方法。

3. 他们在拥堵时的表现
一种常见的误解是,自旋锁或无锁算法“通常更快”,或者它们仅对“非常短的任务”有用(理想情况下,同步对象的持有时间不应超过绝对必要的时间)。
一个重要的区别是不同方法的行为方式出现拥堵时.

设计良好的系统通常拥塞程度较低或没有拥塞(这意味着并非所有线程都尝试同时获取锁)。例如,人们通常会not编写获取锁的代码,然后从网络加载半兆字节的 zip 压缩数据,解码和解析数据,最后在释放锁之前修改共享引用(将数据附加到容器等)。相反,人们获取锁只是为了访问共享资源.
由于这意味着临界区外部的工作比临界区内部的工作多得多,因此线程位于临界区内部的可能性自然相对较低,因此很少有线程同时竞争锁。当然,时不时两个线程会尝试同时获取锁(如果这couldn't碰巧你不需要锁!),但这只是一个例外,而不是“健康”系统中的规则。

在这种情况下,自旋锁greatly优于信号量,因为如果没有锁拥塞,获取自旋锁的开销仅为几十个周期,而上下文切换需要数百/数千个周期,或者丢失时间片剩余部分需要 10-2000 万个周期。

另一方面,如果拥塞程度很高,或者锁被持有很长时间(有时你就是无能为力!),自旋锁会消耗大量的 CPU 周期而一事无成。
在这种情况下,信号量(或互斥量)是更好的选择,因为它允许运行不同的线程useful那段时间的任务。或者,如果没有其他线程有有用的事情可做,它允许操作系统降低 CPU 速度并减少热量/节省能源。

此外,在单核系统上,自旋锁在存在锁拥塞的情况下效率非常低,因为自旋线程将浪费其全部时间来等待不可能发生的状态更改(直到释放线程被调度为止,这没有发生当等待线程正在运行时!)。因此,给定any由于争用量较大,在最好的情况下获取锁大约需要 1 1/2 时间片(假设释放线程是下一个被调度的线程),这不是很好的行为。

4. 如何实施
如今,信号量通常会换行sys_futex在 Linux 下(可选地使用在几次尝试后退出的自旋锁)。
自旋锁通常使用原子操作来实现,而不使用操作系统提供的任何内容。在过去,这意味着使用编译器内部函数或不可移植的汇编器指令。同时,C++11 和 C11 都将原子操作作为语言的一部分,因此除了编写可证明正确的无锁代码的一般困难之外,现在可以以完全可移植且(几乎)的方式实现无锁代码。无痛方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自旋锁与信号量 的相关文章

  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • nslookup 报告“无法解析 '(null)': 名称无法解析”,尽管它成功解析了 DNS 名称

    我在 ubuntu 上 并且正在运行 docker 默认桥接网络 我有 Zookeeper kafka 的容器化版本 以及我编写的与 kafka 对话的应用程序 I do a docker exec it
  • 添加要在给定命令中运行的 .env 变量

    我有一个 env 文件 其中包含如下变量 HELLO world SOMETHING nothing 前几天我发现了这个很棒的脚本 它将这些变量放入当前会话中 所以当我运行这样的东西时 cat env grep v xargs node t
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • 在 Visual Studio 2008 上设置预调试事件

    我想在 Visual Studio 中开始调试程序之前运行一个任务 我每次调试程序时都需要运行此任务 因此构建后事件还不够好 我查看了设置的 调试 选项卡 但没有这样的选项 有什么办法可以做到这一点吗 你唯一可以尝试的 IMO 就是尝试Co
  • 如果使用 SingleOrDefault() 并在数字列表中搜索不在列表中的数字,如何返回 null?

    使用查询正数列表时SingleOrDefault 当在列表中找不到数字时 如何返回 null 或像 1 这样的自定义值 而不是类型的默认值 在本例中为 0 你可以使用 var first theIntegers Cast
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • 如何将单个 char 转换为 int [重复]

    这个问题在这里已经有答案了 我有一串数字 例如 123456789 我需要提取它们中的每一个以在计算中使用它们 我当然可以通过索引访问每个字符 但是如何将其转换为 int 我研究过 atoi 但它需要一个字符串作为参数 因此 我必须将每个字
  • 当操作繁忙时,表单不执行任何操作(冻结)

    我有一个使用 C 的 WinForms 应用程序 我尝试从文件中读取一些数据并将其插入数据表中 当此操作很忙时 我的表单冻结并且无法移动它 有谁知道我该如何解决这个问题 这可能是因为您在 UI 线程上执行了操作 将文件和数据库操作移至另一个
  • 无法使用 wget 在 CentOS 机器上安装 oracle jdk

    我想在CentOS上安装oracle java jdk 8 我无法安装 java jdk 因为当我尝试使用命令安装 java jdk 时 root ADARSH PROD1 wget no cookies no check certific
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • 如何在 Linux shell 中将十六进制转换为 ASCII 字符?

    假设我有一个字符串5a 这是 ASCII 字母的十六进制表示Z 我需要找到一个 Linux shell 命令 它将接受一个十六进制字符串并输出该十六进制字符串代表的 ASCII 字符 所以如果我这样做 echo 5a command im
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj

随机推荐