Thread-lokale Speicherdauer ist ein Begriff, der verwendet wird, um Daten zu bezeichnen, die scheinbar global oder statisch gespeichert sind (aus Sicht der Funktionen, die sie verwenden), aber tatsächlich gibt es eine Kopie pro Thread.
Es ergänzt die aktuelle automatische (existiert während eines Blocks / einer Funktion), statische (existiert für die Programmdauer) und dynamische (existiert auf dem Heap zwischen Zuweisung und Freigabe).
Etwas Thread-Lokales wird bei der Thread-Erstellung ins Leben gerufen und beim Stoppen des Threads entsorgt.
Es folgen einige Beispiele.
Stellen Sie sich einen Zufallszahlengenerator vor, bei dem der Startwert pro Thread verwaltet werden muss. Die Verwendung eines threadlokalen Startwerts bedeutet, dass jeder Thread unabhängig von anderen Threads eine eigene Zufallszahlenfolge erhält.
Wenn Ihr Startwert eine lokale Variable innerhalb der Zufallsfunktion wäre, würde er jedes Mal initialisiert, wenn Sie ihn aufrufen, und Ihnen jedes Mal dieselbe Nummer geben. Wenn es global wäre, würden Threads die Sequenzen des anderen stören.
Ein anderes Beispiel ist so etwas wie strtok
das Speichern des Tokenisierungsstatus auf einer threadspezifischen Basis. Auf diese Weise kann ein einzelner Thread sicher sein, dass andere Threads ihre Tokenisierungsbemühungen nicht vermasseln, während der Status über mehrere Aufrufe an beibehalten werden kann strtok
- dies macht strtok_r
(die thread-sichere Version) im Grunde genommen überflüssig.
Diese beiden Beispiele erlauben die Gewinde lokale Variable zu existieren innerhalb der Funktion , die verwendet es. In Code vor dem Thread ist dies einfach eine statische Variable für die Speicherdauer innerhalb der Funktion. Bei Threads wird dies geändert, um die lokale Speicherdauer des Threads zu gewährleisten.
Ein weiteres Beispiel wäre so etwas wie errno
. Sie möchten nicht, dass separate Threads geändert werden, errno
nachdem einer Ihrer Aufrufe fehlgeschlagen ist, aber bevor Sie die Variable überprüfen können, und Sie möchten dennoch nur eine Kopie pro Thread.
Diese Site enthält eine angemessene Beschreibung der verschiedenen Speicherdauer-Spezifizierer.
strtok
.strtok
ist sogar in einer einzelnen Thread-Umgebung defekt.r
steht das für "Wiedereinsteiger", was nichts mit Thread-Sicherheit zu tun hat. Es ist wahr, dass Sie einige Dinge mit thread-lokalem Speicher threadsicher machen können, aber Sie können sie nicht wieder einführen lassen.strtok
andere Funktionen aufgerufen werden sollten.while (something) { char *next = strtok(whatever); someFunction(next); // someFunction calls strtok }
Wenn Sie eine Variable deklarieren,
thread_local
hat jeder Thread eine eigene Kopie. Wenn Sie namentlich darauf verweisen, wird die dem aktuellen Thread zugeordnete Kopie verwendet. z.BDieser Code gibt "2349", "3249", "4239", "4329", "2439" oder "3429" aus, aber niemals etwas anderes. Jeder Thread hat eine eigene Kopie von
i
, die zugewiesen, inkrementiert und dann gedruckt wird. Der laufende Threadmain
hat auch eine eigene Kopie, die zu Beginn zugewiesen und dann unverändert gelassen wird. Diese Kopien sind völlig unabhängig und haben jeweils eine andere Adresse.Diesbezüglich ist nur der Name besonders - wenn Sie die Adresse einer
thread_local
Variablen verwenden, haben Sie nur einen normalen Zeiger auf ein normales Objekt, das Sie frei zwischen Threads übertragen können. z.BDa die Adresse von
i
an die Thread-Funktion übergeben wird, kann die Kopie deri
Zugehörigkeit zum Haupt-Thread zugewiesen werden, obwohl dies der Fall istthread_local
. Dieses Programm gibt somit "42" aus. Wenn Sie dies tun, müssen Sie darauf achten, dass*p
nach dem Beenden des Threads, zu dem er gehört, nicht darauf zugegriffen wird. Andernfalls erhalten Sie einen baumelnden Zeiger und ein undefiniertes Verhalten, genau wie in jedem anderen Fall, in dem das Objekt, auf das verwiesen wird, zerstört wird.thread_local
Variablen werden "vor der ersten Verwendung" initialisiert. Wenn sie also nie von einem bestimmten Thread berührt werden, werden sie nicht unbedingt jemals initialisiert. Auf diese Weise können Compiler vermeiden, dass jedethread_local
Variable im Programm für einen Thread erstellt wird, der vollständig in sich geschlossen ist und keinen von ihnen berührt. z.BIn diesem Programm gibt es 2 Threads: den Haupt-Thread und den manuell erstellten Thread. Keiner der Threads wird aufgerufen
f
, daher wird dasthread_local
Objekt niemals verwendet. Es ist daher nicht spezifiziert, ob der Compiler 0, 1 oder 2 Instanzen von erstelltmy_class
, und die Ausgabe kann "", "hellohellogoodbyegoodbye" oder "hellogoodbye" sein.quelle
g()
Aufruf an den AnfangthreadFunc
, dann wird der Ausgang sein0304029
einige andere Permutation der Paare oder02
,03
und04
. Das heißt, obwohl 9 zugewiesen wurde,i
bevor die Threads erstellt wurden, erhalten die Threads eine frisch erstellte Kopie voni
wherei=0
. Wenni
mit zugewiesenthread_local int i = random_integer()
, erhält jeder Thread eine neue zufällige Ganzzahl.02
,03
,04
, kann es andere Sequenzen wie020043
Thread-lokaler Speicher ist in jeder Hinsicht wie statischer (= globaler) Speicher, nur dass jeder Thread eine separate Kopie des Objekts hat. Die Lebensdauer des Objekts beginnt entweder beim Thread-Start (für globale Variablen) oder bei der ersten Initialisierung (für blocklokale Statik) und endet, wenn der Thread endet (dh wann
join()
aufgerufen wird).Folglich können nur Variablen deklariert
static
werden , die ebenfalls deklariert werden könntenthread_local
, dh globale Variablen (genauer: Variablen "im Namespace-Bereich"), statische Klassenmitglieder und blockstatische Variablen (in diesem Fallstatic
impliziert).Angenommen, Sie haben einen Thread-Pool und möchten wissen, wie gut Ihre Arbeitslast ausgeglichen wurde:
Dies würde Thread-Nutzungsstatistiken drucken, z. B. mit einer Implementierung wie dieser:
quelle