相关疑难解决方法(0)

带有空闲列表的无锁堆栈:为什么下一个指针不需要是原子的?

无锁堆栈可以实现为单链表。这看起来很简单,直到我们必须考虑在弹出节点后如何处理它们。一种策略是简单地将它们移动到每个堆栈的 LIFO 空闲列表(后续的推送操作可以重用该节点),直到最终所有线程都完成了堆栈,此时单个线程会销毁堆栈中的所有节点和所有节点。空闲列表中的节点。Boost.Lockfree使用这种策略。Chris Wellons 的 C11 实现也是如此。我将参考后者,因为它更容易阅读,而且细节基本相同,因为 C11 原子与 C++11 原子非常相似。

在 Wellons 的实现中(可以在 GitHub 上找到,所有lstack_node对象都是非原子的。特别是,这意味着对对象next成员的所有访问lstack_node都是非原子的。我无法理解的是:为什么此类访问从不相互竞争?

该成员在lstack.c:30 处next读取。它写在lstack.c:39。如果这两行可以在同一个对象上同时执行,则该程序包含竞争。这可能吗?对我来说似乎有可能:lstack_node

  • 线程 1 调用lstack_pop,它调用pop. 它以原子方式将头节点的值加载到局部变量中orig。现在,orig.node是指向刚刚位于堆栈顶部的节点的指针。(请注意,到目前为止,仅修改了局部变量,因此线程 1 迄今为止所做的任何操作都不可能导致任何其他线程中的 CAS 失败。)同时...
  • 线程 2 调用lstack_pop. pop成功并返回node,指向刚刚从堆栈中删除的节点的指针;这与线程 1 中指向的节点相同。orig.node然后它开始调用push以添加node到空闲列表中。自由列表头节点以原子方式加载,并node->next设置为指向自由列表中的第一个节点。
  • 哎呀。这与线程 1 中的读取竞争orig.node->next

难道 Wellons 的实施根本就是错误的吗?我对此表示怀疑。如果他的实现是错误的,那么 Boost …

c c++ boost atomic lock-free

6
推荐指数
2
解决办法
819
查看次数

在固定不同 CPU 的 2 个线程之间传递一些变量的最佳方式

我有一个问题需要了解是否有更好的解决方案。我编写了以下代码,将一些变量从编写器线程传递到读取器线程。这些线程固定到共享相同 L2 缓存的不同 CPU(禁用超线程)。

writer_thread.h

struct a_few_vars {
    uint32_t x1;
    uint32_t x2;

    uint64_t x3;
    uint64_t x4;
} __attribute__((aligned(64)));

volatile uint32_t head;
struct a_few_vars xxx[UINT16_MAX] __attribute__((aligned(64)));
Run Code Online (Sandbox Code Playgroud)

reader_thread.h

uint32_t tail;
struct a_few_vars *p_xxx;
Run Code Online (Sandbox Code Playgroud)

写入线程增加头变量,读取线程检查头变量和尾变量是否相等。如果它们不相等,则按如下方式读取新数据

while (true) {
    if (tail != head) {
        .. process xxx[head] ..
        .. update tail ..
    }
}
Run Code Online (Sandbox Code Playgroud)

性能是迄今为止最重要的问题。我使用的是 Intel Xeon 处理器,读取器线程每次都会从内存中获取 head 值和 xxx[head] 数据。我使用对齐数组来实现无锁

就我而言,是否有任何方法可以尽快将变量刷新到读取器CPU缓存中。我可以从写入器 CPU 触发读取器 CPU 的预取吗?如果存在的话,我可以使用 __asm__ 来使用特殊的英特尔指令。总之,在固定到不同 CPU 的线程之间传递结构中的变量的最快方法是什么?

提前致谢

c x86 intel memory-alignment cpu-cache

2
推荐指数
1
解决办法
246
查看次数

标签 统计

c ×2

atomic ×1

boost ×1

c++ ×1

cpu-cache ×1

intel ×1

lock-free ×1

memory-alignment ×1

x86 ×1