- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我需要在双向链接列表的开头实现子列表的无锁插入。该列表有一个虚拟头,因此每个线程都尝试在头节点之后插入其部分。这种设计对我来说似乎还可以,但是,我没有足够的专业知识来证明它。
struct Node {
std::atomic<Node*> next;
std::atomic<Node*> prev;
};
Node head;
// concurrently insert `first`..`last` sublist after `head`
void insertSublist(Node* first, Node* last) {
first->prev = &head;
Node* current_next = head.next;
while (true) {
last->next = current_next;
if (head.next.compare_exchange_weak(current_next, first)) {
current_next->prev = last;
return;
}
}
}
for (auto* node : nodes_to_be_removed) {
if (node->prev == &head)
continue;
// perform removal
}
node->prev
是最后更改的链接。因此,在更改之后,没有其他线程(除去程序)可以访问该节点或其先前节点的
next
链接。
If you remove a node that an inserter was going to modify (to add the backward link) but hasn't yet
node->prev == &head
可以防止这种情况。是真的吗最佳答案
TL:DR :取决于读者的操作(没有长期损坏),仅插入一项是可以的(但不能进行长期删除),但是如果没有锁定或更加复杂的话,则删除是不可能的,并且对于这种简单的插入算法而言,无疑是最出色的选择。
这是一个双向链接的列表,因此插入不可避免地需要修改其他线程已经可以看到的两个内存位置:head.next
和旧的第一个节点中的.prev
指针。 除非硬件具有DCAS (double-CAS, two separate non-contiguous locations at once),否则无法原子+无锁地完成此操作。如Wikipedia文章所述,它使无锁的双向链接列表变得容易。
m68k在某一点上具有DCAS,但是目前没有主流的CPU体系结构。 ISO C++ 11不会通过std::atomic
公开DCAS操作,因为您必须在没有将所有atomic<T>
设为非锁定的情况下无法在没有该功能的硬件上进行模拟。除了具有事务性内存的硬件以外,英特尔(例如Broadwell和更高版本)在某些最近的x86 CPU上可用,但AMD不可用。在将TM的语法添加到C++方面已有一些工作,请参见https://en.cppreference.com/w/cpp/language/transactional_memory
当然,如果没有事务性内存或DCAS之类的东西,观察者不可能一次原子地观察两个位置。 因此,所有读取列表的线程都应期望它会从列表中更改出来,尤其是在该列表也应支持删除的情况下。
在发布之前在新节点(尚未发布到其他线程)中设置指针显然是一件好事,而您正在这样做。在CAS尝试发布这些新节点之前,first->prev
和last->next
都已正确设置。 CAS具有顺序一致性内存排序,因此可以确保以前的存储对其他线程而言是可见的,而不是它本身。 (因此,出于效率考虑,最好将这些“私有(private)”存储区设置为std::memory_order_relaxed)。
您选择在修改.prev
之后修改旧优先的head
指针很有意义。实际上,您首先要在正向发布,然后在反向发布。 但是请记住,线程在任何时候都可能长时间睡眠,因此假设这始终是暂时的不一致并不是100%安全的。 想象一下,在该函数内部的任何时候都停止在调试器中的一个线程,甚至是单步执行,而其他线程正在运行。在这种情况下,只有两个有趣的操作,CAS和无条件存储到旧的第一个非虚拟节点。
如果线程正在向前遍历,并且取决于能够通过遵循.prev
来返回(而不是在局部变量中记住它自己的前一个),则它看起来就像是再次删除了新节点。它可以找到指向.prev
的head
。这是一个人为的示例,因为如果您想再次找到它,通常记住一个上一个节点通常会更有效,特别是在无锁列表中。但是,也许存在一些非人为的情况,例如一个线程向前移动而另一个线程向后移动,并且可能直接或间接地进行交互,从而可以看到不一致的情况。
只要所有线程都同意修改顺序,我认为插入本身是安全的。仅在头部执行此操作就更易于验证,但我认为允许任意插入点仍然是安全的。
您当前的代码对于同时插入(假设没有删除)看起来是安全的。前向列表可以比后向列表长(在向后列表中可能有多个插入未完成),但是一旦它们全部完成,列表将保持一致。
如果不删除,则对.prev
的每个未决写操作都有一个有效的目的地,并且该目的地是其他线程都不想写入的节点。 无锁单链接列表的插入很容易,并且转发列表(.next
链接)始终是最新的。
因此,每个插入操作在可见到current_next->prev
的存储时,都会将其用作插入点的节点“声明”到反向列表中。 do{}while(!CAS())
循环是一个很好的习惯用法,通常可以简化代码。我削弱了其他操作的内存顺序,尤其是在第一个操作和最后一个操作中,由于要求编译器在存储到其他线程看不到的元素之后使用慢速屏障效率低下,因此降低了它们的内存排序。在x86上,发布存储区是“免费的”(没有额外的障碍),而seq-cst存储区则失去了更高的价格。 (在无竞争的情况下,x86上的seq-cst存储与原子读取-修改-写入的成本大致相同)。
// no change in logic to yours, just minimize memory ordering
// and simplify the loop structure.
void insertSublist(Node* first, Node* last)
{
first->prev.store(&head, std::memory_order_relaxed);
Node* current_next = head.next.load(std::memory_order_relaxed);
do {
// current_next set ahead of first iter, and updated on CAS failure
last->next.store(current_next, std::memory_order_relaxed);
}while (!head.next.compare_exchange_weak(current_next, first));
// acq_rel CAS should work, but leave it as seq_cst just to be sure. No slower on x86
current_next->prev.store(last, std::memory_order_release); // not visible before CAS
}
mfence
指令为x86编译,而不是您的
on the Godbolt compiler explorer 3指令。 (asm的其余部分实际上是相同的,包括一个
lock cmpxchg
。)因此,在无竞争的无RFO的情况下(例如,从同一内核重复插入),速度可能快4倍。或更好,因为
mfence
实际上比Intel CPU上的
lock
前缀还要慢。
do{}while(!CAS)
可以使人们立即更容易阅读和看到逻辑。
head
放在同一缓存行中,因为插入线程将始终需要同时修改它和
head
。也许不是,因为如果核心在获得锁定之后但在将其修改提交给
head
之前有时失去了对该行的所有权,那么您可能最终会对该行产生更多争用。
关于c++ - 这种无锁的dlist插入安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54004268/
我正在学习 Spring 安全性,但我对它的灵活性感到困惑.. 我知道我可以通过在标签中定义规则来保护网址 然后我看到有一个@secure 注释可以保护方法。 然后还有其他注释来保护域(或 POJO)
假设有一个 key 加密 key 位于内存中并且未写入文件或数据库... byte[] kek = new byte[32]; secureRandom.nextBytes(kek); byte[]
我有 Spring Security 3.2.0 RC1 的问题 我正在使用标签来连接我 这表示“方法‘setF
我正在创建一个使用 Node Js 服务器 API 的 Flutter 应用程序。对于授权,我决定将 JWT 与私钥/公钥一起使用。服务器和移动客户端之间的通信使用 HTTPS。 Flutter 应用
在过去的几年里,我一直在使用范围从 Raphael.js 的 javascript 库。至 D3 ,我已经为自己的教育操纵了来自网络各地的动画。我已经从各种 git 存储库下载了 js 脚本,例如 s
在 python 中实现身份验证的好方法是什么?已经存在的东西也很好。我需要它通过不受信任的网络连接进行身份验证。它不需要太高级,只要足以安全地获取通用密码即可。我查看了 ssl 模块。但那个模块让我
我正在尝试学习“如何在 Hadoop 中实现 Kerberos?”我已经看过这个文档 https://issues.apache.org/jira/browse/HADOOP-4487我还了解了基本的
我有一个带有 apache2、php、mysql 的生产服务器。我现在只有一个站点 (mysite.com) 作为虚拟主机。我想把 phpmyadmin、webalizer 和 webmin 放在那里
前些天在网上看到防火墙软件OPNsense,对其有了兴趣,以前写过一个其前面的一个软件M0n0wall( 关于m0n0wa
我在 Spring Boot 和 oauth2(由 Google 提供)上编写了 rest 后端,在 "/login" 上自动重定向。除了 web 的 oauth 之外,我还想在移动后端进行 Fire
我想调用类 Foo,它的构造函数中有抽象类 Base。我希望能够从派生自 Base 的 Derived 调用 Foo 并使用 Derived覆盖方法而不是 Base 的方法。 我只能按照指示使用原始指
如何提高 session 的安全性? $this->session->userdata('userid') 我一直在为我的 ajax 调用扔掉这个小坏蛋。有些情况我没有。然后我想,使用 DOM 中的
我目前正在为某些人提供程序集编译服务。他们可以在在线编辑器中输入汇编代码并进行编译。然后编译它时,代码通过ajax请求发送到我的服务器,编译并返回程序的输出。 但是,我想知道我可以做些什么来防止对服务
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
目前,我通过将 session 中的 key 与 MySQl 数据库中的相同 key 相匹配来验证用户 session 。我使用随机数重新生成 session ,该随机数在每个页面加载时都受 MD5
Microsoft 模式与实践团队提供了一个很棒的 pdf,称为:“构建安全的 asp.net 应用程序”。 microsoft pdf 由于它是为 .Net 1.0 编写的,所以现在有点旧了。有谁知
在 Lua 中,通常会使用 math.random 生成随机值和/或字符串。 & math.randomseed , 其中 os.time用于 math.randomseed . 然而,这种方法有一个
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我们有一个严重依赖 Ajax 的应用程序。确保对服务器端脚本的请求不是通过独立程序而是通过坐在浏览器上的实际用户的好方法是什么 最佳答案 真的没有。 通过浏览器发送的任何请求都可以由独立程序伪造。 归
我正在寻找使用 WebSockets 与我们的服务器通信来实现 web (angular) 和 iPhone 应用程序。在过去使用 HTTP 请求时,我们使用请求数据、url、时间戳等的哈希值来验证和
我是一名优秀的程序员,十分优秀!