线程安全与可重入
Recently, I asked a question, with title as "Is malloc thread safe?", and inside that I asked, "Is malloc re-entrant?"
I was under the impression that all re-entrant are thread-safe.
Is this assumption wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
TL;DR:函数可以是可重入的、线程安全的,或者两者兼而有之。
线程安全和重入非常值得一读。 以下是一些引用:
如果满足以下条件,则函数是线程安全:
如果满足以下条件,则函数是可重入:
作为可能重入的示例,维基百科给出了一个设计为由系统中断调用的函数的示例:假设当另一个中断发生时它已经在运行。 但是,不要仅仅因为您不使用系统中断进行编码就认为您是安全的:如果您使用回调或递归函数,则在单线程程序中可能会出现重入问题。
示例
(根据维基百科文章稍作修改)
示例 1:非线程安全,不可重入
示例 2:线程安全,不可重入
示例 3:非线程安全,不可重入可重入
示例 4:线程安全、可重入
TL;DR: A function can be reentrant, thread-safe, both or neither.
The Wikipedia articles for thread-safety and reentrancy are well worth reading. Here are a few citations:
A function is thread-safe if:
A function is reentrant if:
As examples of possible reentrance, the Wikipedia gives the example of a function designed to be called by system interrupts: suppose it is already running when another interrupt happens. But don't think you're safe just because you don't code with system interrupts: you can have reentrance problems in a single-threaded program if you use callbacks or recursive functions.
Examples
(Slightly modified from the Wikipedia articles)
Example 1: not thread-safe, not reentrant
Example 2: thread-safe, not reentrant
Example 3: not thread-safe, reentrant
Example 4: thread-safe, reentrant
这取决于定义。 例如Qt 使用以下内容:
但他们也警告:
It depends on the definition. For example Qt uses the following:
but they also caution:
可重入函数不依赖于 C 库标头中公开的全局变量。以 C 中的 strtok() 与 strtok_r() 为例。
某些函数需要一个地方来存储“正在进行的工作”、可重入函数允许您在线程自己的存储中指定此指针,而不是在全局中。 由于此存储是调用函数独有的,因此它可以被中断并重新进入(可重入),并且因为在大多数情况下,不需要超出函数实现范围的互斥来实现此功能,它们通常被认为是线程安全的。 然而,定义并不能保证这一点。
然而,errno 在 POSIX 系统上的情况略有不同(并且在解释这一切如何工作时往往是奇怪的):)
简而言之,可重入通常意味着线程安全(如“使用如果您使用线程,则该函数的可重入版本”),但线程安全并不总是意味着可重入(或相反)。 当您考虑线程安全时,您需要考虑并发性。 如果您必须提供锁定和互斥的方法才能使用函数,那么该函数本质上并不是线程安全的。
但是,并非所有功能都需要进行检查。 malloc() 不需要可重入,它不依赖于任何给定线程的入口点范围之外的任何内容(并且本身是线程安全的)。
如果不使用互斥体、futex 或其他原子锁定机制,返回静态分配值的函数不是线程安全的。 然而,如果它们不被打断,则不需要重入。
即:
所以,正如您所看到的,让多个线程在没有某种锁定的情况下使用它将是一场灾难..但它没有可重入的目的。 当动态分配内存在某些嵌入式平台上是禁忌时,您就会遇到这种情况。
重入通常并不意味着线程安全,它取决于传递到函数入口点的定义函数或匿名函数的行为、递归等。
在纯函数式编程中,可 safe'对于并发访问是安全的,这更好地说明了需求。
Re-entrant functions do not rely on global variables that are exposed in the C library headers .. take strtok() vs strtok_r() for example in C.
Some functions need a place to store a 'work in progress' , re-entrant functions allow you to specify this pointer within the thread's own storage, not in a global. Since this storage is exclusive to the calling function, it can be interrupted and re-entered (re-entrant) and since in most cases mutual exclusion beyond what the function implements isn't required for this to work, they are often considered to be thread safe. This isn't, however, guaranteed by definition.
errno, however, is a slightly different case on POSIX systems (and tends to be the oddball in any explanation of how this all works) :)
In short, reentrant often means thread safe (as in "use the reentrant version of that function if you're using threads"), but thread safe does not always mean re-entrant (or the reverse). When you're looking at thread-safety, concurrency is what you need to be thinking about. If you have to provide a means of locking and mutual exclusion to use a function, then the function isn't inherently thread-safe.
But, not all functions need to be examined for either.
malloc()
has no need to be reentrant, it does not depend on anything out of the scope of the entry point for any given thread (and is itself thread safe).Functions that return statically allocated values are not thread safe without the use of a mutex, futex, or other atomic locking mechanism. Yet, they don't need to be reentrant if they're not going to be interrupted.
i.e.:
So, as you can see, having multiple threads use that without some kind of locking would be a disaster .. but it has no purpose being re-entrant. You'll run into that when dynamically allocated memory is taboo on some embedded platform.
In purely functional programming, reentrant often doesn't imply thread safe, it would depend on the behavior of defined or anonymous functions passed to the function entry point, recursion, etc.
A better way to put 'thread safe' is safe for concurrent access , which better illustrates the need.
是的。 所有可重入函数都是线程安全的。 可重入函数属于线程安全函数的范畴。
线程安全函数和可重入函数之间的区别是可重入函数必须仅对本地数据进行操作。 虽然线程安全函数可以对共享数据进行操作,但是对此共享数据的访问必须同步。
Yes. All reentrant functions are thread safe. Reentrant functions come under the category of thread-safe functions.
The difference between thread-safe functions and reentrant functions is reentrant functions must operate on only the local data. Where as thread-safe functions can operate on shared data between however access to this shared data must be synchronized.