当前位置: 首页 > 知识库问答 >
问题:

解释迈克尔

莫河
2023-03-14

我在研究Michael

但我在我的代码中产生了一个竞赛,并认为算法中可能存在竞赛。

我在这里阅读了论文:简单、快速和实用的非阻塞和阻塞并发队列算法,原始的取消排队伪代码如下:

dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
D1:   loop                          // Keep trying until Dequeue is done
D2:      head = Q->Head             // Read Head
D3:      tail = Q->Tail             // Read Tail
D4:      next = head.ptr->next      // Read Head.ptr->next
D5:      if head == Q->Head         // Are head, tail, and next consistent?
D6:         if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
D7:            if next.ptr == NULL  // Is queue empty?
D8:               return FALSE      // Queue is empty, couldn't dequeue
D9:            endif
                // Tail is falling behind.  Try to advance it
D10:            CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
D11:         else                    // No need to deal with Tail
               // Read value before CAS
               // Otherwise, another dequeue might free the next node
D12:            *pvalue = next.ptr->value
               // Try to swing Head to the next node
D13:            if CAS(&Q->Head, head, <next.ptr, head.count+1>)
D14:               break             // Dequeue is done.  Exit loop
D15:            endif
D16:         endif
D17:      endif
D18:   endloop
D19:   free(head.ptr)                // It is safe now to free the old node
D20:   return TRUE                   // Queue was not empty, dequeue succeeded

在我看来,比赛是这样的:

    < li >线程1前进到D3,然后停止。 < li >线程2前进到D3,读取与线程1相同的磁头。 < li >线程2幸运地一直前进到D20,在D19它释放了head.ptr < li >线程1继续前进到D4,尝试读取< code>head.ptr-

对于线程1,我的C代码总是在D4崩溃。

有人能指出我的错误并给出一些解释吗?

共有3个答案

姬昊焱
2023-03-14

是的,问题可能在D19。

正如公认的答案所指出的,不是正常的免费()。

这在导言§1中有所说明

仅当数据结构中没有指针或临时变量指向节点时,才会释放节点。

乜元魁
2023-03-14

这实际上是自MS队列的作者之一Maged Michael引入Hazard Pointers[1]以来多年来一直在探索的非阻塞内存回收问题。

危险指针允许线程保留块,以便其他线程在完成之前不会真正回收它们。但是,此机制会产生不平凡的性能开销。

还有许多基于时代的填海变体,如RCU[2,3],以及最近的基于间隔的填海(IBR)[4]。它们通过保留时代来避免免费使用,并且比危险指针更快。据我所知,基于时代的填海被广泛采用来处理这个问题。

你可以看看下面提到的这些论文,了解更多细节。基于区间的内存回收的论文有很多背景讨论。

这是非阻塞数据结构中的一个普遍问题,我们通常不认为它是数据结构本身的错误——毕竟它只发生在使用手动内存管理的语言中,比如C/C,而不是像Java(顺便说一句,迈克尔

参考:

[1] 危险指针:无锁对象的安全内存回收,Maged M.Michael,IEEE并行和分布式系统事务,2004年。

[2]用于无锁同步的内存回收性能,Thomas E. Hart等人,并行和分布式计算杂志,2007年。

[3]阅读副本更新,Paul E. McKenney等人,渥太华Linux研讨会,2002年。

[4]基于区间的内存回收,郝森文等,第23届ACM SIGPLAN并行编程原理与实践研讨会(PPoPP)论文集,2018。

司寇高洁
2023-03-14

谢谢你,非常有趣的主题!这看起来绝对像一个错误,但是这篇论文的一位作者断言,他们的Free()不是我们都生活在一起的正常的Free(),而是一些神奇的Free(),所以没有错误。太棒了。

见https://web.archive.org/web/20190905090735/http://blog.shealevy.com/2015/04/23/use-after-free-bug-in-maged-m-michael-and-michael-l-scotts-non-blocking-concurrent-queue-algorithm/

希望没有人在没有彻底分析的情况下将其投入生产。

 类似资料:
  • 在github上,您可以查看到存储库的流量,如下所示: 现在我的问题是关于克隆体和独特克隆体的巨大差异。如果我对此理解正确的话,那就意味着4919人创造了22.374个克隆人。为什么有些人会克隆一个存储库这么多次? 我的实际回购看起来不那么令人印象深刻,但差异仍然很大。(150ish克隆大约30个唯一克隆,所以每个克隆者取5个克隆)。 这让我想知道什么算克隆。如果它是相关的,那么它是针对go库的,

  • 面试推迟了半个小时,没有原因,打电话让等,过程还不错。  1.介绍实习。  2.挑一个项目介绍。  3.tcp udp 区别。  4.面向对象三大特征。  5.如何保证线程安全,这块我感觉我说偏了。  6.设计复合图形求面积。  10.18面完等结果 #迈普通信#

  • 本文向大家介绍详解 c# 克隆,包括了详解 c# 克隆的使用技巧和注意事项,需要的朋友参考一下 克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象。正如其名,犹如一个模子雕刻而出。克隆类型分为两种:浅克隆、深克隆。 1、浅克隆 浅克隆方式是最简单、最直接的方式。只需要类实现接口ICloneable(在命名空间System.Runtime.InteropServices下)

  • 我在使用Lombok的注释时遇到了问题,因为jar似乎还没有被导入到project中: 上面写着: 无法解析方法信息(java.lang.String) 编译时: 错误:(6,1)java:package-org。slf4j不存在 我做到了: 将lombok的依赖项插入pom 我在这里寻找解决方案: Lombok补充道,但Intellij IDEA中没有认可的能手和二传手 在IntelliJ ID

  • 处理注释 mixin 的杰克逊代码是否可以由第三方重用来混合非杰克逊注释? 处理混合蛋白的核心杰克逊类是什么?

  • 更新:该错误似乎与我拥有的.babelrc文件有关: 当我移除这个文件时,错误就消失了。 原帖: 我正在使用React与包裹捆绑器。首先,我有一个问题,与我的包裹版本和@babel/preset-env(无效版本:未定义)不兼容有关。 我通过在package.json文件中添加一个resolutions标记来解决问题,以强制使用不需要version对象的以前版本的Babel。这起作用了,但现在我在