2009-05-13 25 views
75

Recientemente, hice una pregunta, con el título como "Is malloc thread safe?", y en el interior le pregunté: "¿Malloc re-entrant?"Threadsafe vs re-entrant

Tenía la impresión de que todos los reentrantes son seguros para subprocesos.

¿Es esta suposición incorrecta? funciones

Respuesta

38

reentrante no se basan en variables globales que se exponen en las cabeceras de la biblioteca de C .. tomar strtok() vs strtok_r (por ejemplo) en C.

Algunas funciones necesitan un lugar para almacenar un ' work in progress ', las funciones de reentrante le permiten especificar este puntero dentro del propio almacenamiento del subproceso, no en un global. Como este almacenamiento es exclusivo de la función de llamada, puede interrumpirse y reingresar (reingreso) y dado que en la mayoría de los casos la exclusión mutua más allá de lo que la función implementa no es necesaria para que funcione, a menudo se consideran ser hilo seguro. Sin embargo, esto no está garantizado por definición.

errno, sin embargo, es un caso ligeramente diferente en sistemas POSIX (y tiende a ser la excéntrica en cualquier explicación de cómo funciona todo esto) :)

En resumen, reentrante menudo significa hilo de seguridad (como en "use la versión reentrante de esa función si está utilizando subprocesos"), pero la seguridad de subprocesos no siempre significa volver a entrar (o al revés). Cuando mira la seguridad de hilo, concurrencia es lo que debe tener en cuenta. Si tiene que proporcionar un medio de bloqueo y exclusión mutua para usar una función, entonces la función no es intrínsecamente segura para subprocesos.

Pero no es necesario examinar todas las funciones. malloc() no necesita ser reentratado, no depende de nada fuera del alcance del punto de entrada para un hilo determinado (y es en sí mismo seguro para subprocesos).

Las funciones que devuelven valores asignados estáticamente son no hilo seguro sin el uso de un mutex, futex u otro mecanismo de bloqueo atómico. Sin embargo, no necesitan ser reentrantes si no van a ser interrumpidos.

es decir .:

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; 
} 

Por lo tanto, como se puede ver, que tienen múltiples hilos de usar que sin algún tipo de bloqueo sería un desastre .. pero no tiene el propósito de ser re-entrante. Se encontrará con eso cuando la memoria asignada dinámicamente es tabú en alguna plataforma incorporada.

En la programación puramente funcional, reentrante menudo no implica hilo de seguridad, que dependerá del comportamiento de las funciones definidas o anónimos pasados ​​al punto de entrada de la función, la recursión, etc.

Una mejor manera de poner 'hilo seguro' es seguro para acceso concurrente, que ilustra mejor la necesidad.

+2

reentrante no implica thread-safe. Las funciones puras implican seguridad de hilo. –

+0

Gran respuesta Tim. Solo para aclarar, mi comprensión de su "a menudo" es que la seguridad de subprocesos no implica reentrada, pero también reentrada no implica seguro de subprocesos. ¿Sería capaz de encontrar un ejemplo de una función de reentrada que * no * es segura para subprocesos? – Riccardo

+0

@ Tim Post "En resumen, reentrante a menudo significa hilo seguro (como en" usar la versión reentrante de esa función si estás utilizando subprocesos "), pero seguro de subprocesos no siempre significa volver a entrar". qt [dice] (http://qt-project.org/doc/qt-4.8/threads-reentrancy.html) al revés: "Por lo tanto, una función de hilo seguro siempre es reentrante, pero una función de reentrada no siempre es un hilo seguro." – 4pie0

53

Depende de la definición.Por ejemplo Qt uses la siguiente:

  • Una función * thread-safe puede ser llamado simultáneamente desde varios subprocesos, incluso cuando las invocaciones utilizan datos compartidos, ya que todas las referencias a los datos compartidos se serializan.

  • A reentrant función también se puede llamar simultáneamente desde varios subprocesos, pero solo si cada invocación utiliza sus propios datos.

Por lo tanto, una función thread-safe siempre es reentrante, sino una función reentrante no siempre es seguro para subprocesos.

Por extensión, se dice que una clase es reentrante si sus funciones miembro pueden llamarse de forma segura desde varios subprocesos, siempre que cada subproceso use una instancia diferente de la clase. La clase es thread-safe si sus funciones miembro se pueden llamar de forma segura desde varios subprocesos, incluso si todos los subprocesos utilizan la misma instancia de la clase.

pero también Precaución:

Nota: Terminología en el dominio multihilo no ha sido estandarizada por completo. POSIX usa definiciones de reentrantes y subprocesos que son algo diferentes para sus API de C. Al usar otras librerías de clases C++ orientadas a objetos con Qt, asegúrese de que se entiendan las definiciones.

+1

Esta definición de reentrada es demasiado fuerte. – qweruiop

+4

Votación a la baja. Una función de hilo seguro NO siempre es reentrante. –

+0

Una función es tanto reentrante como segura para subprocesos si no utiliza ninguna var global/estática. Thread-safe: cuando muchos hilos ejecutan su función al mismo tiempo, ¿hay alguna carrera? Si usa var global, use lock para protegerlo. por lo que es seguro para subprocesos. reentrant: si se produce una señal durante la ejecución de su función, y vuelve a llamar a su función en la señal, ¿es seguro? en tal caso, no hay múltiples hilos. Lo mejor es que no uses ninguna variable estática/global para hacerla reentrada, o como en el ejemplo 3. –

42

TL; DR: Una función puede ser reentrante, hilo seguro, ambos o ninguno.

Los artículos de Wikipedia para thread-safety y reentrancy son dignos de leer. Aquí hay algunas citas:

Una función es thread-safe si:

sólo manipula compartía estructuras de datos en una forma que garantice la ejecución segura por múltiples hilos al mismo tiempo.

Una función es reentrada si:

se puede interrumpir en cualquier momento durante su ejecución y luego llamó con seguridad de nuevo ("volvió a entrar") antes de que sus invocaciones anteriores ejecución completa .

Como ejemplos de posible reentrada, la Wikipedia da el ejemplo de una función diseñada para ser llamada por interrupciones del sistema: supongamos que ya se está ejecutando cuando ocurre otra interrupción. Pero no piense que está seguro solo porque no codifica las interrupciones del sistema: puede tener problemas de reentrada en un programa de un solo subproceso si usa devoluciones de llamada o funciones recursivas.

La clave para evitar confusiones es que la reentrada se refiere a solo una ejecución de subproceso. Es un concepto desde el momento en que no existían sistemas operativos multitarea.

Ejemplos

(ligeramente modificado a partir de los artículos de Wikipedia)

Ejemplo 1: no seguro para subprocesos, no reentrante

/* 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; 
} 

Ejemplo 2: thread-safe , no reentrante

/* 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; 
} 

Ejemplo 3: no seguro para subprocesos, reentrante

/* 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; 
} 

Ejemplo 4: thread-safe, reentrante

/* 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; 
} 
+3

Sé que se supone que debo comentar solo para agradecer, pero esta es una de las mejores ilustraciones que presentan el diferencias entre las funciones de reentrada y seguridad de subprocesos. En particular, ha utilizado términos claros muy concisos, y eligió una gran función de ejemplo para distinguir entre las 4 categorías. ¡Así que gracias! – ryyker

+0

Una función es tanto reentrante como segura para subprocesos si no utiliza var global/estática. Thread-safe: cuando muchos hilos ejecutan su función al mismo tiempo, ¿hay alguna carrera? Si usa var global, use lock para protegerlo. por lo que es seguro para subprocesos. reentrant: si se produce una señal durante la ejecución de su función, y vuelve a llamar a su función en la señal, ¿es seguro? en tal caso, no hay múltiples hilos.no debe usar ninguna variable estática/global para hacerla reentrada ... –

+0

Me parece que el ejemplo 3 no es reentrante: si un manejador de señal, interrumpiendo después de 't = * x', llama a' swap() ', luego 't' será anulado, lo que lleva a resultados inesperados. – rom1v

Cuestiones relacionadas