【问题标题】:Memory management in a lock free queue无锁队列中的内存管理
【发布时间】:2011-06-23 22:14:37
【问题描述】:

我们一直在寻找在我们的代码中使用无锁队列来减少当前实现中单个生产者和消费者之间的锁争用。那里有很多队列实现,但我还不太清楚如何最好地管理节点的内存管理。

例如,生产者是这样的:

queue.Add( new WorkUnit(...) );

消费者看起来像:

WorkUnit* unit = queue.RemoveFront();
unit->Execute();
delete unit;

我们目前使用内存池进行分配。您会注意到生产者分配内存而消费者删除它。由于我们使用的是池,因此我们需要向内存池添加另一个锁以正确保护它。这似乎首先否定了无锁队列的性能优势。

到目前为止,我认为我们的选择是:

  • 实现无锁内存池。
  • 转储内存池并依赖线程安全分配器。

还有其他我们可以探索的选择吗?我们试图避免实现无锁内存池,但我们可能会走这条路。

谢谢。

【问题讨论】:

  • 它会改变你的语义,但你可以尝试排队对象而不是指针。

标签: c++ lock-free


【解决方案1】:

只有生产者才能创建对象并在不再需要时销毁它们。消费者只能使用对象并将其标记为已使用。这才是重点。在这种情况下,您不需要共享内存。这是我所知道的高效无锁队列实现的唯一方法。

阅读this 详细描述此类算法的精彩文章。

【讨论】:

  • 正如 Herb Sutter 的跟进清楚地表明,那篇文章有太多的并发错误,甚至都不好笑; drdobbs.com/cpp/210600279
  • @Aaron Klotz:嗯。本文只是对内存问题的出色处理的一个例证。问题正是关于这个。当然,我们应该使用原子迭代器(我记得他在文章随附的源代码中使用了自己的列表实现)和内存屏障来避免重新排序。我同意,这不是一个简单的话题,而是实现无锁的东西所面临的众所周知的问题。
  • 尽管原始文章存在缺陷,但 Sutter 的后续和后续实施确实说明了我在寻找什么。我会再研究一下这个。
【解决方案2】:

另一种可能性是为每个线程设置一个单独的内存池,这样只有一个线程在使用堆,您可以避免分配锁定。

这让你可以管理一个无锁函数来释放一个块。幸运的是,这很容易管理:您为每个堆维护一个空闲块的链接列表。您将块自己的地址放入(您将用作的内存)链表的链接字段,然后与持有链表头部的指针进行原子交换。

【讨论】:

    【解决方案3】:

    你应该看看英特尔的 TBB。我不知道商业项目的成本是多少(如果有的话),但他们已经有了并发队列、并发内存分配器之类的东西。

    你的队列接口看起来也很狡猾——例如,你的 RemoveFront() 调用——如果队列是空的怎么办? newdelete 调用看起来也很多余。英特尔的 TBB 和微软的 PPL(包含在 VS2010 中)不会遇到这些问题。

    【讨论】:

    • 这不是实际的代码,它只是为了说明内存分配是如何工作的。 TBB 和 PPL 不是有效的选项。我们被困在这里想出一个本土解决方案。
    【解决方案4】:

    我不确定您的具体要求是什么,但如果您有生产者创建数据并将此数据推送到队列中的场景,并且您有一个消费者获取并使用这些数据然后将其销毁,那么您只需要谨慎行事队列,或者您可以创建自己的单链表,在这种情况下是安全的。

    • 创建列表元素
    • 在列表元素中追加数据
    • 将下一个元素的指针从 null 更改为列表元素(或其他语言中的等效项)

    Consumer 可以以任何方式实现,并且大多数链表默认情况下对于这种操作是安全的(但请检查该实现方式)

    在这种情况下,消费者应该释放这个内存,或者它可以以同样的方式将它返回给生产者设置预定义的标志。生产者不需要检查所有标志(如果它们超过 1000 个)来查找哪个桶是空闲的,但是这个标志可以像树一样实现,启用 log(n) 搜索可用池。我确信这可以在 O(1) 时间内完成而无需锁定,但不知道如何

    【讨论】:

      【解决方案5】:

      我也有同样的顾虑,所以我编写了自己的无锁队列(单生产者,单消费者)来为您管理内存分配(它分配一系列连续的块,有点像 std::vector )。

      我最近发布了code on GitHub。 (另外,我posted on my blog 关于它。)

      如果您在堆栈上创建节点,将其入队,然后将其出队到堆栈上的另一个节点,您根本不需要使用指针/手动分配。此外,如果您的节点为构造和赋值实现了移动语义,它将自动移动而不是复制:-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-21
        • 2011-08-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多