Memory 多线程堆管理

Memory 多线程堆管理,memory,operating-system,heap,Memory,Operating System,Heap,在C/C++中,我可以在一个线程中分配内存,在另一个线程中删除它。然而,无论何时从堆请求内存,堆分配器都需要遍历堆以找到大小合适的空闲区域。两个线程如何有效地访问同一堆而不损坏堆?(这是通过锁定堆来实现的吗?是的,通常必须锁定对堆的访问。任何时候你有一个共享资源,该资源需要得到保护;内存是一种资源。这在很大程度上取决于您的平台/操作系统,但我相信这在主要系统上通常是可以的。C/C++不定义线程,因此默认情况下我认为答案是“heap不受保护”,即必须对堆访问进行某种多线程保护 然而,至少在linu

在C/C++中,我可以在一个线程中分配内存,在另一个线程中删除它。然而,无论何时从堆请求内存,堆分配器都需要遍历堆以找到大小合适的空闲区域。两个线程如何有效地访问同一堆而不损坏堆?(这是通过锁定堆来实现的吗?

是的,通常必须锁定对堆的访问。任何时候你有一个共享资源,该资源需要得到保护;内存是一种资源。

这在很大程度上取决于您的平台/操作系统,但我相信这在主要系统上通常是可以的。C/C++不定义线程,因此默认情况下我认为答案是“heap不受保护”,即必须对堆访问进行某种多线程保护

然而,至少在linux和gcc中,我相信启用-pthread会自动为您提供这种保护

此外,还有另一个相关问题:


这是一个操作系统问题,因此答案取决于操作系统

在Windows上,每个进程都有自己的堆。这意味着同一进程中的多个线程(默认情况下)共享一个堆。因此,操作系统必须对其分配和释放调用进行线程同步,以防止堆损坏。如果您不喜欢随后可能发生的争论,您可以使用。您甚至可以重载malloc(在C中)和new(在C++中)来调用它们。

是的,支持多线程代码的“普通”堆实现必须包含某种类型的锁定,以确保正确的操作。在相当极端的条件下(大量堆活动),这可能成为瓶颈;在这种情况下,可以使用更专门的堆(通常提供某种线程本地堆)。我已经用英特尔TBB的。还有其他的malloc示例,它们是在考虑多线程扩展的情况下实现的

单线程和多线程感知malloc之间的一些时间比较。

我找到了链接


基本上,堆可以分为多个竞技场。当请求内存时,会依次检查每个竞技场是否已锁定。这意味着不同的线程可以同时安全地访问堆的不同部分。自由游戏有点复杂,因为每个自由游戏都必须从分配给它的竞技场中释放出来。我认为一个好的实现会让不同的线程默认到不同的领域,以尽量减少争用。

一般来说,您不需要担心内存分配器的线程安全。所有标准内存分配器——即MacOS、Windows、Linux等附带的内存分配器——都是线程安全的。锁是提供线程安全性的标准方式,尽管可以编写只使用原子操作而不使用锁的内存分配器


现在是一个完全不同的问题,这些内存分配器是否可以扩展;也就是说,它们的性能是否与执行内存操作的线程数无关?在大多数情况下,答案是否定的;它们要么速度变慢,要么会消耗更多内存。两个维度(速度和空间)中的第一个可伸缩分配器是(我写的);MacOSX分配器的灵感来源于此,并在文档中引用了它,但囤积速度更快。还有其他线程,包括谷歌的tcmalloc。

即使每个线程都管理自己的内存?听起来效率太低了。@deus:不,但你描述的情况不是这样的。你说线程共享内存。(在另一个线程中删除。)重新标记,因为这实际上与任何特定的编程语言无关。不完全正确。只有当堆增长时(这涉及到在新内存页中分页),操作系统才会参与。实际上,堆的管理取决于malloc/new的C/C++实现。只是出于兴趣,gcc和MSVC的malloc策略是什么?好问题。不太了解MSVC的CRT,但gcc通常与使用ptmalloc:的glibc相关。上面的计时链接很好地显示了这种扩展,这就解释了为什么我自己用TBB的分配器做的实验有时使事情变得更好,有时更糟。@doron Windows Vista和更新版本使用低碎片堆,这可能使标准malloc在多线程程序中工作得很好。你能提供一些关于囤积所采用的一般策略的信息吗?内存是以称为超级块的块进行管理的,这些块包含大小相同的对象。每个线程都会获得一定数量的锁(线程本地),这意味着没有锁或争用。线程被多路复用到每个CPU堆上,这些堆包含超级块。超级块的分配一次只能由一个线程完成,从而限制了错误共享。当CPU堆变为空时,囤积通过将大部分空超级块移动到共享堆来限制内存消耗——在确保渐近最优内存消耗的同时限制争用。看见