std :: list,std :: vector方法和malloc()

时间:2009-07-19 14:19:43

标签: c++ stl

在中断处理程序下使用stl:list和stl :: vector类型我想避免malloc()调用。

问题:在STL列表和向量中阻止malloc()调用的最佳方法是什么?是否足以创建具有预定义大小的结构,然后避免推/弹/擦除调用?

提前谢谢

6 个答案:

答案 0 :(得分:14)

std::liststd::vector等STL容器的构造函数接受Allocator类型。通过提供自己的分配器而不是使用默认值,您可以控制容器分配内存的方式。很少使用此选项,但在实时环境中使用您自己的分配器是此功能有用的一个很好的示例(并证明STL的设计人员做得非常好)。

自定义分配器类型的要求在C++ standard

中的20.1.6中描述

答案 1 :(得分:7)

听起来你想在初始化代码中预先分配内存,这样你的中断处理程序就可以避免堆分配。我假设您存储在这些容器中的元素本身不会执行任何堆分配,因为这会使答案复杂化。

您可以通过调用std::vector方法为reserve()预分配内存。像push_back()pop()insert()erase()这样的方法会操纵向量的大小(当前包含的元素数)。当新尺寸大于当前容量时,它们仅影响容量(它有空间的元件数量)。 reserve(x)确保容量大于或等于x,必要时增加容量。 (另请注意,唯一降低向量容量的操作是swap(),因此您不必担心erase()会降低向量的容量。)

这种方法不适用于std::list,但还有另一种方法:通过将列表元素插入“备用”列表来预分配列表元素。使用splice()方法将它们从“备用”列表移动到“主要”列表,而不是插入新元素。不使用擦除元素,而是使用splice()方法将它们从“主”列表移动到“备用”列表。

答案 2 :(得分:4)

作为推荐:我们在工作场所使用其他答案中提到的两种方法:

  • 自定义分配器:对于我们的内存泄漏跟踪系统,我们的仪器分析器和一些其他系统,我们预先分配和/或“池”(参见例如boost :: pool)使用提供的分配分配器 - 通常用于std :: set或std :: map,但std :: list的原理相同。
  • 保留/调整大小:对于std :: vectors,我们提前保留或调整大小(差异很重要,但两者都可以帮助避免将来的分配)是非常常见的做法。

大多数情况下,我们会做这两件事以避免碎片,减少分配器开销,消除复制增加的惩罚等。但有时(特别是对于仪表分析器)我们希望在中断处理程序期间绝对避免分配。 / p>

但是,通常我们会以其他方式避免中断和分配问题:

  • 进入/离开:尽量避免在中断期间执行除标志或普通副本之外的任何操作;有时静态(或预分配)缓冲区是比STL容器好得多的解决方案。持中断时间过长通常会导致灾难。
  • 在alloc / free期间禁用中断:在我们分配/释放时,中断排队,而不是立即调度 - 这是我们正在使用的CPU的一个功能。结合有选择地增加禁用/排队范围的策略(例如std :: list操作),我们有时可以使用中断处理程序作为生产者,一切 - 其他作为消费者模型,而不是覆盖分配器。如果我们正在从std :: list中消费某些东西(例如从网络硬件接收到的消息),那么中断会在尽可能短的时间内排队,同时弹出我们即将处理的内容的副本

请注意,无锁数据结构可以替代此处的第二个项目符号,我们尚未设置和完成概要分析以查看它是否有帮助。无论如何,设计自己的东西都很棘手。

对于中断处理程序来说,偏执狂是勇敢的一部分:如果你不确定你正在做什么会起作用,有时以完全不同的方式处理问题要好得多。

答案 3 :(得分:3)

要添加的另一件事:const std::vector 不会导致分配。因此,如果您的中断处理代码没有更改向量,请将其声明为const,编译器将使vector保持不变。

答案 4 :(得分:2)

作为onebyone.livejournal.com mentioned,C ++标准没有提及任何关于中断处理程序的内容。它确实讨论了信号处理程序,但即使这样,它也是一个非常灰色的区域。关于在信号处理程序中唯一能够保证在所有符合要求的C / C ++实现中具有良好定义的行为的事情是分配给sig_atomic_t类型的变量并返回,例如:

sig_atomic_t flag = 0;

// This signal handler has well-defined behavior
void my_signal_handler(int signum)
{
    flag = 1;
}

int main(void)
{
    signal(SIGINT, &my_signal_handler);

    while(1)
    {
        doStuff();
        if(flag)
        {
            flag = 0;
            actuallyHandleSignalNow();
        }
    }

    return 0;
}

虽然在实践中,你几乎总能在信号处理程序中做更多的事情。

答案 5 :(得分:1)

对于std::vector来说应该足够了。尽管如此,我认为没有任何保证。内存分配被视为实现细节。如果您可以将自己限制为特定大小,我建议使用简单的静态数组。这样,您可以对实际发生的情况进行细粒度控制。