同步访问双向链表

2024-04-20

我正在尝试在 pthreads 环境中用 C 实现一个(特殊类型的)双向链表,但仅使用 C 包装的同步指令(如原子 CAS 等)而不是 pthread 原语。 (列表的元素是固定大小的内存块,几乎肯定无法容纳pthread_mutex_t等等。)我实际上不需要完整的任意双向链表方法,只需要:

  • 在列表末尾插入
  • 从列表开头删除
  • 基于指向要删除的成员的指针在列表中的任意点进行删除,该指针是从除遍历列表之外的源获取的。

因此,也许描述此数据结构的更好方法是队列/fifo,并且可以删除队列中的项目。

是否有一个标准方法来同步这个?我陷入了可能的僵局问题,其中一些问题可能是所涉及的算法所固有的,而另一些问题可能源于这样一个事实:我试图在一个有限的空间中工作,而我能做的事情受到其他限制。

Edit:特别是,如果要同时删除相邻对象,我该怎么办?据推测,在删除对象时,您需要获取列表中前一个和下一个对象的锁,并更新它们的 next/prev 指针以指向彼此。但如果任何一个邻居已经被锁定,这将导致死锁。我试图找出一种方法,使任何/所有发生的删除都可以遍历列表的锁定部分,并确定当前正在删除过程中的最大子列表,然后锁定与该子列表相邻的节点,以便整个子列表被整体删除,但我的头开始受伤..:-P

结论(?):后续,我确实有一些想要运行的代码,但我也对理论问题感兴趣。每个人的答案都非常有帮助,并结合了我在这里表达的限制之外的详细信息(您really不想知道要删除的元素的指针来自哪里以及其中涉及的同步!)我决定暂时放弃本地锁定代码并专注于:

  • 使用大量较小的列表,每个列表都有单独的锁。
  • 最大限度地减少持有锁的指令数量,并在获取锁之前(以安全的方式)访问内存,以减少持有锁时发生页面错误和缓存未命中的可能性。
  • 测量人为高负载下的争用并评估该方法是否令人满意。

再次感谢所有给出答案的人。如果我的实验进展不顺利,我可能会回到概述的方法(尤其是弗拉德的)并重试。


为什么不直接应用粗粒度锁呢?只需锁定整个队列即可。

更复杂的(但不一定更有效,取决于您的使用模式)解决方案是使用读写锁,分别用于读取和写入。


在我看来,使用无锁操作对于您的情况来说并不是一个好主意。想象一下某个线程正在遍历您的队列,同时“当前”项目被删除。无论您的遍历算法拥有多少额外链接,所有这些项目都可能被删除,因此您的代码将没有机会完成遍历。


比较和交换的另一个问题是,使用指针,您永远不知道它是否真的指向同一个旧结构,或者旧结构已被释放,并且在同一地址分配了一些新结构。对于您的算法来说,这可能是也可能不是问题。


对于“本地”锁定的情况(即,可以单独锁定每个列表项),一个想法是使锁有序。对锁进行排序可确保不会出现死锁。所以你的操作是这样的:

通过指针p删除previous item:

  1. 锁定 p,检查(可能使用项目中的特殊标志)该项目仍在列表中
  2. lock p->next,检查它是否不为零并且在列表中;这样你就可以确保 p->next->next 不会同时被删除
  3. 锁定 p->下一个->下一个
  4. 在 p->next 中设置一个标志,表明它不在列表中
  5. (p->下一个->下一个->上一个, p->下一个->上一个) = (p, null); (p->下一个, p->下一个->下一个) = (p->下一个->下一个, null)
  6. 释放锁

在开头插入:

  1. 锁头
  2. 在新项目中设置标志,表明它在列表中
  3. 锁定新项目
  4. 锁头->下一步
  5. (头->下一个->上一个,新->上一个) = (新,头); (新->下一个,头)=(头,新)
  6. 释放锁

这似乎是正确的,但我没有尝试这个想法。

本质上,这使得双链表像单链表一样工作。


如果您没有指向前一个列表元素的指针(当然通常是这种情况,因为实际上不可能使此类指针保持一致状态),您可以执行以下操作:

通过指针c删除要删除的项:

  1. 锁定c,检查它是否仍然是列表的一部分(这必须是列表项中的标志),如果不是,操作失败
  2. 获取指针 p = c->prev
  3. 解锁c(现在,c可能被其他线程移动或删除,p也可能从列表中移动或删除)[为了避免c的释放,您需要有类似共享指针的东西或至少一种此处列表项的重新计数]
  4. lock p
  5. 检查 p 是否是列表的一部分(可以在步骤 3 后将其删除);如果没有,解锁p并从头开始
  6. 检查p->next是否等于c,如果不等于,则解锁p并从头开始重新启动[这里我们也许可以优化重新启动,不确定ATM]
  7. 锁定p->下一个;这里你可以确定 p->next==c 并没有被删除,因为删除 c 需要锁定 p
  8. 锁定 p->下一个->下一个;现在所有的锁都已被占用,所以我们可以继续
  9. 设置标志 c 不是列表的一部分
  10. 执行惯例 (p->next, c->next, c->prev, c->next->prev) = (c->next, null, null, p)
  11. 释放所有锁

请注意,仅拥有指向某个列表项的指针无法确保该项目不会被释放,因此您需要进行某种重新计数,以便该项目在您尝试锁定它时不会被销毁。


请注意,在最后一个算法中,重试次数是有限的。确实,新项不能出现在c的左边(插入是在最右边的位置)。如果我们的步骤 5 失败,因此我们需要重试,这只能是由于同时从列表中删除 p 造成的。这种删除最多可以发生 N-1 次,其中 N 是 c 在列表中的初始位置。当然,这种最坏的情况不太可能发生。

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

同步访问双向链表 的相关文章

随机推荐

  • 为什么 CSS 中的背景:url 不适用于 Django?

    我的导航栏有以下 CSS 代码 footer navigation background 1841c8 url images nav background gif height 40px padding 0 0 0 20px 但是 当我启动
  • 一个域上的 ProxyPass 和 DocumentRoot

    假设我有以下配置
  • 如何在 Objective-C (iPhone) 中连接字符串? [复制]

    这个问题在这里已经有答案了 可能的重复 如何在 Objective C 中连接字符串 https stackoverflow com questions 510269 how do i concatenate strings in obje
  • Pycharm:遇到调试点时如何专注于编辑器

    我使用的是mac Pycharm版本为2018 2 4社区版 当我使用调试器运行调试会话并命中调试点时 我必须使用鼠标单击编辑器才能在编辑器上键入代码 如果我不这样做并直接敲击键盘 Mac 会发出一些 bing 声 表明键盘输入对任何应用程
  • SignalR:连接建立时服务器如何正确订阅组

    我已经查看了几个地方 但仍然找不到关于如何使用组的明确说明 我正在使用组进行过滤 仅将消息传递给客户端子集 我想将客户端加入服务器端的组OnConnected事件 客户端不需要知道它属于哪个组 问题 我是否也应该覆盖OnReconnecte
  • Firebase google-services.json 文件

    我对 Firebase google services json 文件有疑问 每次我添加或更改某些内容时 例如 如果我添加新的 SHA1 指纹 是否需要再次下载该文件并将其放在 Android 项目的应用程序文件夹中 或者只使用第一次创建的
  • 更改 Firebase 电子邮件不会更新providerData

    我在我的 iOS 应用程序中使用 Firebase 用户使用 Firebase 的电子邮件和密码身份验证登录 目前 我正在创建允许用户更改电子邮件和密码的功能 我注意到使用成功更改电子邮件地址后changingEmailForUser 电子
  • CSS 边距为负而不移动父容器

    我正在尝试进入此页面 http musicaladvocacy org http musicaladvocacy org 显示 Home 灰色渐变中的白色容器 的区域向上移动约 60 px 但正如您所看到的 它同时将父容器向上移动 我只是想
  • 单选按钮上的 jQuery .change()

    我一定在这里遗漏了一些明显的东西 我无法理解 改变 http api jquery com change 触发单选按钮 我有下面的代码here http bjmarine net test html
  • CSS 图像精灵

    使用CSS图像精灵的唯一好处是减少http请求吗 或者还有其他好处吗 还有一种简单的方法可以确定要显示精灵的哪个区域的时间吗 正如您所说 主要优点之一是减少对服务器的请求数量 提高响应时间 特别是在加载大量小图像时 但这并不是人们使用精灵的
  • 计算 R 中数值向量的位数

    我在 R 中有一个数字向量 c 0 9 0 81 我想提取该向量中每个元素的位数 该命令将返回 在这种情况下 1 and 2因为数字是9 and 81 有方便的方法吗 另外 如果结果是1 如何扩展到两位数 例如 我希望返回的向量是 c 0
  • 坚持/提交在 Spring JPA JUnit 的测试环境中不起作用

    我正在尝试设置基本的 JPA 插入测试 但数据库中没有保存任何内容 数据库是Postgresql Hibernate 用作持久性提供者 提前谢谢了 Entity public class Currency Id GeneratedValue
  • 使用 Node.js 中的 fast-csv 包读取和写入 CSV

    我正在尝试编写一个简单的节点程序 该程序读取 csv 文件 提取列 比如第二列 并将其写入另一个 CSV 文件 我正在将内容读取到数组中 然后将该数组写入文件 每一步的阶段和数据 输入文件 123 456 789 abc def ghi 2
  • 如果不创建插件,则无法在 eclipse 中使用 JFace 和 SWT

    免责声明 这是 NET GUI 试图解决 JAVA 问题的典型案例 问题描述 我正在尝试使用 JFace 和 SWT 构建一个非常简单的 GUI 代码很简单 有很多教程 但不那么简单的是我似乎无法让 JFace 和 SWT 在插件项目之外工
  • React 元标签不适用于 Facebook

    我已经克隆了该应用程序https github com alanbsmith react node example https github com alanbsmith react node example并尝试使用反应头盔 https
  • 使用ggplot2和facet_wrap显示不同的轴标签

    我有一个包含不同变量和不同单位的时间序列 我想在同一个绘图上显示它们 ggplot 不支持多轴 正如这里所解释的 https stackoverflow com questions 3099219 plot with 2 y axes on
  • Chart.getSelection() 无法与谷歌条形图正常工作

    drawBarChart function data few statements goes here which sets options which are being passed to chartDraw i e t options
  • 使用 PHP 查找给定字符串中所有可能的 2 个字母组合 [重复]

    这个问题在这里已经有答案了 我试图找到给定字符串的所有可能的 2 个字母组合 有没有比按位置应用子字符串然后再次调用该函数更快的方法 以下是我正在尝试的 function permute str if strlen str lt 2 ret
  • 创建 Java 泛型类时尖括号中的波形符意味着什么?

    我正在阅读一些 JMockit 示例并发现以下代码 final List
  • 同步访问双向链表

    我正在尝试在 pthreads 环境中用 C 实现一个 特殊类型的 双向链表 但仅使用 C 包装的同步指令 如原子 CAS 等 而不是 pthread 原语 列表的元素是固定大小的内存块 几乎肯定无法容纳pthread mutex t等等