3 ответов:
функции повторного входа не полагаются на глобальные переменные, представленные в заголовках библиотеки C.. взять, что strtok() против strtok_r (), например, в с.
некоторые функции нуждаются в месте для хранения "незавершенного производства", функции re-entrant позволяют указать этот указатель в собственном хранилище потока, а не в глобальном. Поскольку это хранилище является эксклюзивным для вызывающей функции, его можно прервать и повторно поступил (re-entrant) и так как в большинстве случаев взаимные исключение за пределами того, что реализует функция, не требуется для этой работы, они часто считаются потокобезопасным. Однако это не гарантируется по определению.
errno, однако, является немного другим случаем в системах POSIX (и имеет тенденцию быть странным в любом объяснении того, как все это работает):)
короче говоря, reentrant часто означает потокобезопасность (как в "Использовать реентерабельную версию этой функции, если вы используете потоки"), но потокобезопасность не всегда означает повторный вход (или наоборот). Когда вы смотрите на потокобезопасность,параллелизм это то, о чем вам нужно думать. Если вы должны предоставить средства блокировки и взаимного исключения для использования функции, то функция по своей сути не является потокобезопасной.
но, не все функции должны быть рассмотрены либо.
malloc()Не нужно быть реентерабельным, он не зависит ни от чего из области точки входа для любого заданного поток (и сам по себе потокобезопасен).функции, которые возвращают статически выделенные значения не потокобезопасность без использования мьютекса, фьютекса или другого атомарного механизма блокировки. Тем не менее, им не нужно быть реентерабельными, если они не собираются прерываться.
т. е.:
static char *foo(unsigned int flags) { static char ret[2] = { 0 }; if (flags & FOO_BAR) ret[0] = 'c'; else if (flags & BAR_FOO) ret[0] = 'd'; else ret[0] = 'e'; ret[1] = 'A'; return ret; }Итак, как вы можете видеть, использование нескольких потоков без какой-либо блокировки было бы катастрофой .. но у него нет цели быть реентерабельной. Вы столкнетесь с это когда динамически выделяемая память является табу на некоторой встроенной платформе.
в чисто функциональном программировании реентерабельность часто не подразумевают потокобезопасность, это будет зависеть от поведения определенных или анонимных функций, передаваемых в точку входа функции, рекурсии и т. д.
лучший способ поставить "потокобезопасный" - это безопасный для одновременного доступа, что лучше иллюстрирует необходимость.
Это зависит от определения. Например Qt использует следующее:
потокобезопасная * функция может вызываться одновременно из нескольких потоков, даже если вызовы используют общие данные, поскольку все ссылки на общие данные сериализуются.
A reentrant функция также может быть вызвана одновременно из нескольких потоков, но только если каждый вызов использует свой собственный данные.
следовательно, a потокобезопасным функция всегда реентерабельна, но a reentrant функция не всегда потокобезопасна.
по расширению, класс называется reentrant если его функции-члены можно безопасно вызывать из нескольких потоков, если каждый поток использует другой экземпляр класса. Класс-это потокобезопасным если его функции-члены могут быть вызваны безопасно из нескольких потоков, даже если все потоки используют один и тот же экземпляр класса.
но они также предупреждают:
Примечание: терминология в многопоточном домене не полностью стандартизирована. В POSIX использует определения реентерабельность и потокобезопасность, которые несколько отличаются своими C-интерфейсы. При использовании других объектно-ориентированных библиотек классов C++ с Qt убедитесь, что определения понятны.
TL; DR: функция может быть реентерабельной, потокобезопасной, обеих или ни одной.
статьи Википедии потокобезопасность и reentrancy хорошо стоит читать. Вот несколько цитат:
функции потокобезопасным если:
он только манипулирует общими структурами данных в способ, который гарантирует безопасное выполнение несколькими нити при этом время.
функции reentrant если:
он может быть прерван в любой момент во время его выполнения а потом благополучно позвонил снова ("повторно вошел") перед своим предыдущие вызовы завершают выполнение.
в качестве примеров возможного повторного входа Википедия приводит пример функции, предназначенной для вызова системными прерываниями: предположим, что она уже запущена, когда происходит другое прерывание. Но не думай ... вы в безопасности только потому, что вы не кодируете с системными прерываниями: у вас могут быть проблемы с повторным доступом в однопоточной программе, если вы используете обратные вызовы или рекурсивные функции.
ключом для избежания путаницы является то, что реентерабельность относится к выполняется только один поток. Это понятие из того времени, когда никаких многозадачных операционных систем не существовало.
примеры
(немного изменено из Википедии статьи)
Пример 1: не потокобезопасный, не реентерабельный
/* As this function uses a non-const global variable without any precaution, it is neither reentrant nor thread-safe. */ int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }Пример 2: потокобезопасный, не реентерабельный
/* We use a thread local variable: the function is now thread-safe but still not reentrant (within the same thread). */ __thread int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; }Пример 3: не потокобезопасный, реентерабельный
/* We save the global state in a local variable and we restore it at the end of the function. The function is now reentrant but it is not thread safe. */ int t; void swap(int *x, int *y) { int s; s = t; t = *x; *x = *y; *y = t; t = s; }Пример 4: потокобезопасный, реентерабельный
/* We use a local variable: the function is now thread-safe and reentrant, we have ascended to higher plane of existence. */ void swap(int *x, int *y) { int t; t = *x; *x = *y; *y = t; }
Comments