malloc线程安全吗?


Answers:


47

我在某处读到,如果使用-pthread进行编译,则malloc变为线程安全的。我非常确定它的实现取决于,因为malloc是ANSI C,而线程不是。

如果我们在说gcc:

在x86和AMD64上,使用-pthread和malloc()进行编译和链接将是线程安全的。

http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thread/2431a99b9bdcef11/ea800579e40f7fa4

另一种意见,更有见地

glibc-2.2 +的{malloc,calloc,realloc,free,posix_memalign}是线程安全的

http://linux.derkeiler.com/Newsgroups/comp.os.linux.development.apps/2005-07/0323.html


15
“我在某处阅读...”的质量确实不符合公认的答案。;-)
R .. GitHub停止帮助ICE,

12
无论他提供什么参考链接。
链ro

3
注意,简单地添加-pthreads并不会自动使malloc线程安全。如果在信号处理程序中调用malloc,可能会发生各种疯狂的废话。
DIMMSum

2
从信号处理程序调用malloc不安全;很少。它可能在某些情况下工作,但不是法律或便携式据我所知(见en.cppreference.com/w/c/program/signalman7.org/linux/man-pages/man7/signal-safety.7.html
Oliver Seiler

103

问题:“ malloc可重入”吗?
答:不,不是。这是使例行重入的定义。

没有任何常见的malloc版本可以重新输入它(例如,从信号处理程序中输入)。请注意,可重入例程可能不使用锁,并且几乎所有存在的malloc版本都使用锁(这使它们成为线程安全的)或全局/静态变量(使它们成为线程不安全不可重入的)。

到目前为止,所有答案都回答“ malloc线程安全吗?”,这是一个完全不同的问题。对于这个问题,答案是它取决于您的运行时库,并且可能取决于您使用的编译器标志。在任何现代UNIX上,默认情况下都会获得线程安全的malloc。在Windows中,使用/MT/MTd/MD/MDd标志来获得线程安全的运行时库。


12
重入例程可以使用锁,只要该锁是可重入的即可。这使得锁必须大致等效于递归或错误检查互斥体,但是还增加了规定:采用单个锁的原子操作必须导致锁结构对于具有单个锁的新持有锁处于一致状态参考。当然,你也必须处理利用锁定保护的状态已经被中断的代码在同一个线程中处理该部分修饰,但仍然折返锁可以是一个构建块的情况下...
R. GitHub STOP HELPING ICE

1
我相信/ MT / MTd / MD等只是意味着使用release / debug / dynamic或static CRT版本,而不是说malloc是否是线程安全的(它实际上调用了HeapAlloc,它没有可序列化的可选标志)
paulm '16

13

这是一个很老的问题,我想根据当前的情况带来新鲜感。

是的,当前malloc()是线程安全的。

GNU C库参考手册glibc-2.20 [released 2014-09-07]

void * malloc (size_t size)

初步:MT安全 ...

... 1.2.2.1 POSIX安全概念:

...在存在其他线程的情况下,可以安全地调用MT安全或线程安全功能。MT-Safe中的MT代表多线程。

成为MT-Safe并不意味着功能是原子的,也不意味着它使用POSIX向用户提供的任何内存同步机制。甚至有可能依次调用MT-Safe功能不会产生MT-Safe组合。例如,让一个线程一个接一个地调用另一个MT-Safe功能并不能保证等同于两个功能的组合被原子执行的行为,因为其他线程中的并发调用可能会以破坏性的方式进行干扰。

可跨库接口内联函数的整个程序优化可能会暴露不安全的重新排序,因此不建议跨GNU C库接口执行内联。在整个程序优化中,不能保证已记录的MT安全状态。但是,用户可见的标头中定义的函数被设计为可以安全地进行内联。


9

这是glibc的malloc.c的摘录:

线程安全:线程安全,除非定义了NO_THREADS

假设默认情况下未定义NO_THREADS,则malloc至少在Linux上是线程安全的。


7

是的,在POSIX.1-2008malloc是线程安全的。

2.9.1线程安全

POSIX.1-2008的本卷定义的所有功能都应是线程安全的,但以下功能1不必是线程安全的。

[不包含的功能列表malloc]


7

如果您使用的是GLIBC,答案是:是的,但是。

具体来说,是的,但是请注意,尽管malloc和free是线程安全的,但调试功能不是。

特别是,极其有用的mtrace(),mcheck()和mprobe()函数不是线程安全的。您将从GNU项目中看到的最短,最直接的答案之一,在此进行解释:

https://sourceware.org/bugzilla/show_bug.cgi?id=9939

您将需要考虑其他技术,例如ElectricFence,valgrind,dmalloc等。

因此,如果您的意思是“ malloc()和free()函数是线程安全的”,答案是肯定的。但是,如果您的意思是“整个malloc / free套件都是线程安全的”,答案是否定的。


6

简短的回答:是的,从C11开始,它是C标准的第一个版本,其中包含线程的概念,malloc并且要求朋友保持线程安全。许多同时包含线程和C运行时的操作系统早在C标准之前就做出了保证,但是我不准备向所有人宣誓。然而,malloc并不需要也不要求再入友。

这意味着,可以安全地同时从多个线程进行调用mallocfree而不必担心锁定,只要您不违反其他任何内存分配规则即可(例如free,对所返回的每个指针仅调用一次malloc)。但它是不是安全的,从可能已中断的呼叫信号处理程序调用这些函数mallocfree在线程处理信号。有时,使用超越ISO C的功能,您可以保证处理信号的线程不会中断对malloc或的调用。free,例如使用sigprocmasksigpause,但是除非没有其他选择,否则请不要这样做,因为很难完全正确。


引文长答案:C标准在2011年修订版中增加了线程的概念(链接到文档N1570,这是与2011年标准的官方文本最接近的近似文本,该文本免费公开提供)。在该修订版中,第7.1.4节第5段规定:

除非在随后的详细描述中另有明确说明,否则库函数应防止出现以下情况的数据争用:库函数不得直接或间接访问除当前线程以外的线程可访问的对象,除非通过函数的参数直接或间接访问对象。库函数不得直接或间接修改可由当前线程以外的线程访问的对象,除非通过函数的非常量参数直接或间接访问这些对象。如果对象对用户不可见并且可以防止数据竞争,则实现可以在线程之间共享其内部对象。

[脚注:例如,这意味着不允许实现将静态对象用于内部目的而不进行同步,因为即使在没有在线程之间显式共享对象的程序中,它也可能导致数据争用。同样,不允许memcpy的实现复制目标对象指定长度以外的字节,然后恢复原始值,因为如果程序在线程之间共享这些字节,则可能导致数据争用。

据我了解,这是一种冗长的说法,它要求C标准定义的库函数必须是线程安全的(通常意义上:您可以从多个线程中同时调用它们,而无需自己进行任何锁定) ,只要它们最终不会与作为参数传递的数据发生冲突),除非特定功能的文档明确指出并非如此。

然后,7.22.3p2确认malloc,calloc,realloc,aligned_alloc和free特别是线程安全的:

为了确定数据竞争的存在,内存分配函数的行为就好像它们仅访问可通过其参数访问的存储位置,而不访问其他静态持续时间存储一样。但是,这些功能可能会明显地修改它们分配或取消分配的存储。释放内存区域p的对free或realloc的调用与分配该区域p的全部或部分的任何分配调用同步。此同步发生在通过分配函数对p进行任何访问之后,以及在通过分配函数进行任何此类访问之前。

对比在7.24.5.8p6中关于strtok的说法,它不是,而且从来都不是线程安全的:

不需要strtok函数来避免数据与strtok函数的其他调用争用。

[脚注:可以使用strtok_s函数来避免数据争用。]

(脚注上的注释:请勿使用strtok_s,请使用strsep。)

较旧的C标准版本对线程安全性一无所知。但是,他们确实提到了可重入性,因为信号一直是C标准的一部分。这就是他们所说的,可以追溯到最初的1989年ANSI C标准(该文档的措词与来年的ISO C标准几乎完全相同,但章节编号却大不相同):

如果[a]信号不是通过调用abort或raise函数的结果发生的,则如果信号处理程序调用标准库中除信号函数本身以外的任何函数或引用具有静态存储持续时间other的任何对象,则该行为未定义而不是通过为volatile sig_atomic_t类型的静态存储持续时间变量分配值。此外,如果对信号函数的此类调用导致SIG_ERR返回,则errno的值不确定。

这是说,C库函数的长篇大论方式为可重入作为一般规则要求。在C11,7.14.1.1p5中仍然出现非常相似的措辞:

如果[a]信号不是通过调用abort或raise函数的结果发生的,则如果信号处理程序引用具有静态或线程存储持续时间的任何对象,而该对象不是非锁定原子对象,则该行为是未定义的,除非通过分配声明为volatile sig_atomic_t的对象的值,或者信号处理程序调用标准库中的任何函数(异常终止函数,_Exit函数,quick_exit函数或信号函数除外),该函数的第一个参数等于与引起处理程序调用的信号。此外,如果对信号函数的此类调用导致SIG_ERR返回,则errno的值不确定。

[脚注:如果异步信号处理程序生成了任何信号,则该行为是不确定的。]

POSIX与C库的总体大小相比需要更长的时间,但仍然很短,可以从“异步信号处理程序”安全地调用函数列表,并且还详细定义了信号可能“在其他情况下发生”的情况。而不是调用中止或引发功能的结果。” 如果您要对信号做一些微不足道的操作,则可能是在编写要在具有Unix性质的OS上运行的代码(与Windows,MVS或嵌入的,可能没有完整托管C实现的代码相反)首先),您应该熟悉POSIX的要求以及ISO C的要求。


1

这取决于您使用的C运行时库的实现。例如,如果您使用的是MSVC,则有一个编译器选项,可让您指定要使用哪个版本的库(即,是否通过运行安全性来支持多线程的运行时库)。


1

不,它不是线程安全的。您的C库中实际上可能有malloc_lock()andmalloc_unlock()函数。我知道这些存在于Newlib库中。我必须使用它为我的处理器实现互斥体,该互斥体在硬件中是多线程的。


5
在符合任何主要线程标准(例如POSIX)的任何系统上,malloc的任何实现都是线程安全的。
R .. GitHub停止帮助ICE,

1

malloc和free不能重入,因为它们使用静态数据结构来记录哪些内存块是空闲的。结果,没有分配或释放内存的库函数可重入。


1

我建议阅读

§31.1线程安全性(和可重入性)

本书“ Linux编程接口”一书中,它解释了线程安全性和可重入性之间的区别,以及malloc

摘抄:

如果一个函数可以同时被多个线程安全地调用,则该函数被称为线程安全的。相反,如果一个函数不是线程安全的,那么当它在另一个线程中执行时,我们不能从一个线程中调用它。

....
该函数说明了一个函数不是线程安全的典型原因:它采用了所有线程共享的全局或静态变量。

...
尽管使用关键部分来实现线程安全相对于使用每个功能的互斥锁是一项重大改进,但由于锁定和解锁互斥锁会付出一定的代价,因此效率仍然有些低下。可重入函数无需使用互斥即可实现线程安全。它通过避免使用全局变量和静态变量来做到这一点。

...
但是,并非所有功能都可以重入。通常的原因如下:

  • 就其性质而言,某些功能必须访问全局数据结构。malloc库中的函数提供了一个很好的示例。这些函数维护堆上空闲块的全局链接列表。该malloc库的功能通过使用互斥锁而成为线程安全的。

....

绝对值得一读。

为了回答您的问题,malloc线程安全但不可重入。


0

2
groups.google.com/group/comp.lang.c.moderated/browse_thread/…所述,这并不完全正确。不能保证malloc是也不是线程安全的。它取决于实现。
马修·弗拉申

2
安全不是局部的;如果实现可能不安全,那么我的解释是那是不安全的。海报未指定平台;因此,不能保证安全。
保罗·索尼尔

6
任何给定的实现都是安全的还是不安全的。说一个未知的实现是不安全的,并不比说它是安全的更真实。正确的答案是它与实现有关。
ChrisW

3
@ChrisW:由于OP没有指定实现,并且由于安全是绝对条件(只能打破一个例外;只有一个不安全的动作才能使某些事情“不安全”),所以我决定回答说这是不安全的。原始张贴者可以为自己确定正确的答案。
保罗·索尼尔

3
@ChrisW-俄罗斯轮盘赌是安全的。
Oktalist 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.