Les fonctions rentrantes ne reposent pas sur des variables globales qui sont exposées dans les en-têtes de la bibliothèque C .. prenez strtok () vs strtok_r () par exemple en C.
Certaines fonctions ont besoin d'un endroit pour stocker un 'travail en cours', les fonctions réentrantes vous permettent de spécifier ce pointeur dans le propre stockage du thread, pas dans un global. Étant donné que ce stockage est exclusif à la fonction appelante, il peut être interrompu et réintégré (rentrant) et comme dans la plupart des cas, l'exclusion mutuelle au-delà de ce que la fonction implémente n'est pas nécessaire pour que cela fonctionne, ils sont souvent considérés comme étant thread safe . Ce n'est cependant pas garanti par définition.
errno, cependant, est un cas légèrement différent sur les systèmes POSIX (et a tendance à être étrange dans toute explication de la façon dont tout cela fonctionne) :)
En bref, réentrant signifie souvent thread-safe (comme dans «utiliser la version réentrante de cette fonction si vous utilisez des threads»), mais thread safe ne signifie pas toujours rentrant (ou l'inverse). Lorsque vous regardez la sécurité des threads, la concurrence est ce à quoi vous devez penser. Si vous devez fournir un moyen de verrouillage et d'exclusion mutuelle pour utiliser une fonction, la fonction n'est pas intrinsèquement thread-safe.
Mais toutes les fonctions n'ont pas besoin d'être examinées pour l'un ou l'autre. malloc()
n'a pas besoin d'être réentrant, il ne dépend de rien hors de la portée du point d'entrée pour un thread donné (et est lui-même thread-safe).
Les fonctions qui renvoient des valeurs allouées statiquement ne sont pas sûres pour les threads sans l'utilisation d'un mutex, d'un futex ou d'un autre mécanisme de verrouillage atomique. Pourtant, ils n'ont pas besoin d'être réentrants pour ne pas être interrompus.
c'est à dire:
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;
}
Donc, comme vous pouvez le voir, avoir plusieurs threads utiliser cela sans une sorte de verrouillage serait un désastre ... mais cela n'a aucun but d'être rentrant. Vous rencontrerez cela lorsque la mémoire allouée dynamiquement est taboue sur certaines plates-formes intégrées.
Dans la programmation purement fonctionnelle, réentrant n'implique souvent pas de thread safe, cela dépendrait du comportement de fonctions définies ou anonymes passées au point d'entrée de la fonction, de la récursivité, etc.
Une meilleure façon de mettre «thread safe» est sans danger pour l'accès simultané , ce qui illustre mieux le besoin.
TL; DR: Une fonction peut être réentrante, thread-safe, les deux ou aucun.
Les articles de Wikipédia sur la sécurité des threads et la réentrance méritent d'être lus. Voici quelques citations:
Une fonction est thread-safe si:
Une fonction est réentrante si:
Comme exemples de réentrance possible, Wikipédia donne l'exemple d'une fonction conçue pour être appelée par des interruptions du système: supposons qu'elle soit déjà en cours d'exécution lorsqu'une autre interruption se produit. Mais ne pensez pas que vous êtes en sécurité simplement parce que vous ne codez pas avec des interruptions système: vous pouvez avoir des problèmes de réentrance dans un programme à un seul thread si vous utilisez des rappels ou des fonctions récursives.
Exemples
(Légèrement modifié à partir des articles de Wikipedia)
Exemple 1: pas thread-safe, pas réentrant
/* 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; }
Exemple 2: thread-safe, non réentrant
/* 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; }
Exemple 3: non thread-safe, réentrant
/* 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; }
Exemple 4: thread-safe, réentrant
/* 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; }
la source
t = *x
, appelleswap()
, alorst
sera surchargé, conduisant à des résultats inattendus.swap(5, 6)
est interrompu par unswap(1, 2)
. Aprèst=*x
,s=t_original
ett=5
. Maintenant, après l'interruption,s=5
ett=1
. Cependant, avant le secondswap
retour, il restaurera le contexte, créantt=s=5
. Maintenant, nous revenons au premierswap
avect=5 and s=t_original
et continuons aprèst=*x
. Ainsi, la fonction semble être rentrante. N'oubliez pas que chaque appel obtient sa propre copie d's
allocation sur pile.Cela dépend de la définition. Par exemple, Qt utilise ce qui suit:
mais ils mettent également en garde:
la source