Recentemente, ho posto una domanda, con titolo "Il thread malloc è sicuro?" , e al suo interno ho chiesto: "Il malloc rientra?"
Avevo l'impressione che tutti i rientranti fossero thread-safe.
Questa ipotesi è sbagliata?
Recentemente, ho posto una domanda, con titolo "Il thread malloc è sicuro?" , e al suo interno ho chiesto: "Il malloc rientra?"
Avevo l'impressione che tutti i rientranti fossero thread-safe.
Questa ipotesi è sbagliata?
Risposte:
Le funzioni di rientro non si basano su variabili globali che sono esposte nelle intestazioni della libreria C .. prendi strtok () vs strtok_r () per esempio in C.
Alcune funzioni necessitano di una posizione in cui memorizzare un "lavoro in corso", le funzioni rientranti consentono di specificare questo puntatore all'interno della memoria del thread, non in una globale. Poiché questa memorizzazione è esclusiva della funzione chiamante, può essere interrotta e reinserita (rientro) e poiché nella maggior parte dei casi l'esclusione reciproca oltre a ciò che la funzione implementa non è richiesta affinché questa funzioni, sono spesso considerati thread-safe . Tuttavia, questo non è garantito per definizione.
errno, tuttavia, è un caso leggermente diverso sui sistemi POSIX (e tende ad essere il bizzarro in ogni spiegazione di come funziona tutto questo) :)
In breve, rientrante spesso significa thread-safe (come in "usa la versione rientrante di quella funzione se stai usando thread"), ma thread-safe non sempre significa rientrante (o il contrario). Quando guardi alla sicurezza dei thread, la concorrenza è ciò a cui devi pensare. Se è necessario fornire un mezzo di blocco e di mutua esclusione per utilizzare una funzione, la funzione non è intrinsecamente thread-safe.
Ma non tutte le funzioni devono essere esaminate neanche per. malloc()
non ha bisogno di essere rientrante, non dipende da nulla al di fuori dell'ambito del punto di ingresso per un dato thread (ed è esso stesso thread-safe).
Le funzioni che restituiscono valori allocati staticamente non sono thread-safe senza l'uso di un mutex, futex o altri meccanismi di blocco atomico. Tuttavia, non hanno bisogno di essere rientrati se non verranno interrotti.
cioè:
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;
}
Quindi, come puoi vedere, avere più thread che lo usano senza un qualche tipo di blocco sarebbe un disastro .. ma non ha alcuno scopo essere rientranti. Ti imbatterai in questo quando la memoria allocata dinamicamente è tabù su alcune piattaforme incorporate.
Nella programmazione puramente funzionale, rientrante spesso non implica thread safe, dipenderebbe dal comportamento di funzioni definite o anonime passate al punto di ingresso della funzione, ricorsione, ecc.
Un modo migliore per mettere "thread safe" è sicuro per l'accesso simultaneo , il che illustra meglio la necessità.
TL; DR: una funzione può essere rientrante, thread-safe, entrambe o nessuna delle due.
Vale la pena leggere gli articoli di Wikipedia sulla sicurezza dei thread e sul rientro . Ecco alcune citazioni:
Una funzione è thread-safe se:
manipola solo le strutture di dati condivise in un modo che garantisce un'esecuzione sicura da parte di più thread contemporaneamente.
Una funzione è rientrante se:
può essere interrotto in qualsiasi momento durante la sua esecuzione e quindi richiamato di nuovo in sicurezza ("reinserito") prima che le sue precedenti invocazioni completino l'esecuzione.
Come esempi di possibile rientro, Wikipedia fornisce l'esempio di una funzione progettata per essere chiamata da interruzioni di sistema: supponiamo che sia già in esecuzione quando si verifica un'altra interruzione. Ma non pensare di essere al sicuro solo perché non codifichi con interruzioni di sistema: puoi avere problemi di rientro in un programma a thread singolo se usi callback o funzioni ricorsive.
La chiave per evitare confusione è che rientrante si riferisce a un solo thread in esecuzione. È un concetto del tempo in cui non esistevano sistemi operativi multitasking.
Esempi
(Leggermente modificato dagli articoli di Wikipedia)
Esempio 1: non thread-safe, non rientrante
/* 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;
}
Esempio 2: thread-safe, non rientrante
/* 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;
}
Esempio 3: non thread-safe, rientrante
/* 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;
}
Esempio 4: thread-safe, rientrante
/* 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;
}
t = *x
, chiama swap()
, allora t
verrà sovrascritto, portando a risultati inaspettati.
swap(5, 6)
essere interrotta da un file swap(1, 2)
. Dopo t=*x
, s=t_original
e t=5
. Ora, dopo l'interruzione, s=5
e t=1
. Tuttavia, prima che il secondo swap
ritorni ripristinerà il contesto, rendendo t=s=5
. Ora torniamo al primo swap
con t=5 and s=t_original
e continuiamo dopo t=*x
. Quindi, la funzione sembra essere rientrata. Ricorda che ogni chiamata riceve la propria copia di s
allocato in pila.
Dipende dalla definizione. Ad esempio Qt utilizza quanto segue:
Una funzione thread-safe * può essere chiamata simultaneamente da più thread, anche quando le chiamate utilizzano dati condivisi, poiché tutti i riferimenti ai dati condivisi sono serializzati.
Una funzione rientrante può anche essere chiamata simultaneamente da più thread, ma solo se ogni chiamata utilizza i propri dati.
Quindi, una funzione thread-safe è sempre rientrante, ma una funzione rientrante non è sempre thread-safe.
Per estensione, si dice che una classe rientri se le sue funzioni membro possono essere chiamate in sicurezza da più thread, purché ogni thread utilizzi un'istanza diversa della classe. La classe è thread-safe se le sue funzioni membro possono essere chiamate in modo sicuro da più thread, anche se tutti i thread utilizzano la stessa istanza della classe.
ma avvertono anche:
Nota: la terminologia nel dominio multithreading non è completamente standardizzata. POSIX utilizza definizioni di rientrante e thread-safe che sono leggermente diverse per le sue API C. Quando si usano altre librerie di classi C ++ orientate agli oggetti con Qt, assicurarsi che le definizioni siano comprese.