10.9 Erzeugen von Thread-spezifischen Daten (TSD-Data)
Bei einem Aufruf von Funktionen werden die lokalen Daten auf dem Stack abgelegt und auch wieder abgeholt. Bei den Threads kann man ja solch langlebige Daten entweder mit zusätzlichen Argumenten an die einzelnen Threads weitergeben oder aber in globalen Variablen speichern. Wenn Sie aber z. B. vorhaben, eine Bibliothek für den Multithread-Gebrauch zu schreiben, ist dies nicht mehr möglich, da man ja hierbei die Argumentzahl nicht mehr verändern kann, damit auch ältere Programme ohne Threads diese Bibliothek verwenden können und man auch nicht weiß, wie viele Threads die Bibliotheksfunktionen nutzen werden, kann man nun mal keine Aussage darüber machen, wie groß die globalen Daten sein sollen.
Das Ganze vielleicht noch etwas vereinfacht erklärt, es ist einfach nicht möglich, dass globale und statische Variablen unterschiedliche Werte in den verschiedenen Threads haben können. Aus diesem Grund wurden »Schlüssel« eingeführt - oder auch Thread-spezifische Daten (kurz TSD-Data). Dabei handelt es sich um eine Art Zeiger, der immer auf die Daten verweist, die dem Thread gehören, der eben einen entsprechenden »Schlüssel« benutzt. Beachten Sie allerdings, dass hierbei immer mehrere Threads den gleichen »Schlüssel« benutzen – nicht jeder Thread einen extra »Schlüssel«!
Dabei bekommt jeder Thread einen privaten Speicherbereich mit einem eigenen Schlüssel zugeteilt. Dies kann man sich gerne als ein Array von void-Zeigern vorstellen, auf die der Thread mit »seinem« Schlüssel zugreifen kann.
Hierzu die Funktionen, womit Sie Thread-spezifische Daten erzeugen bzw. die damit arbeiten können.
#include <pthread. h.>
int pthread_key_create(
pthread_key_t *key, void (*destr_function) (void*) );
int pthread_key_delete( pthread_key_t key );
int pthread_setspecific(
pthread_key_t key, const void *pointer );
void * pthread_getspecific(pthread_key_t key);
Mit pthread_key_create() erzeugen Sie einen neuen TSD-Schlüssel mit der Speicherstelle key des Schlüssels und (als zweites Argument) entweder NULL oder die Funktion, um den Speicher der Daten wieder freizugeben, eine Exit-Handler-Funktion, wenn Sie so wollen.
Mit der Funktion pthread_setspecific() können Sie Daten mit dem TSD-Schlüssel assoziieren. Sie legen damit praktisch die TSD-Daten für den TSD-Schlüssel key über den Zeiger pointer fest.
Mit der Funktion pthread_getspecific() kann man die Daten aus dem TSD-Schlüssel auslesen.
Mit dem folgenden Beispiel werden MAX_THREADS Threads erzeugt, von denen jeder Thread eine Thread-eigene Datei mittels TDS-Daten erzeugt. Im Beispiel wird hierbei nur protokolliert, dass der Thread gestartet und wieder beendet wurde. Zwischen den beiden Zeilen sollten Sie die eigentliche Arbeit des Threads eintragen. Fehler oder sonstige Meldungen dieser Arbeit können Sie ebenfalls wieder mit »thread_write« in die für den Thread vorgesehene Datei schreiben. Dass diese Funktion »nur« mit einem einfachen String aufgerufen werden kann, ist dem TSD-Schlüssel zu verdanken, der in der Funktion »thread_write« mittels pthread_getspecific() eingelesen wird. Ein simples Grundgerüst eben, womit Sie ohne großen Aufwand Thread-eigene Logdateien verwenden können.
/* thread17.c */
#include <pthread. h.>
#include <stdio.h>
#include <stdlib.h>
#define MAX_THREADS 3
/* TSD-Datenschlüssel */
static pthread_key_t tsd_key;
/* Schreibt einen Text in eine Datei für
* jeden aktuellen Thread */
void thread_write (const char* text) {
/* TSD-Daten lesen */
FILE* th_fp = (FILE*) pthread_getspecific (tsd_key);
fprintf (th_fp, "%s\n", text);
}
/* Am Ende den Zeiger auf die Datei(en) schließen */
void thread_close (void* th_fp) {
fclose ((FILE*) th_fp);
}
void* thread_tsd (void* args) {
char th_fpname[20];
FILE* th_fp;
/* Einen Thread-spezifischen Dateinamen erzeugen */
sprintf(th_fpname,"thread%d.thread",(int) pthread_self());
/* Die Datei öffnen */
th_fp = fopen (th_fpname, "w");
if( th_fp == NULL )
pthread_exit(NULL);
/* TSD-Daten zu TSD-Schlüssel festlegen */
pthread_setspecific (tsd_key, th_fp);
thread_write ("Thread wurde gestartet ...\n");
/* Hier kommt die eigentliche Arbeit des Threads hin */
thread_write("Thread ist fertig ...\n");
pthread_exit(NULL);
}
int main (void) {
int i;
pthread_t threads[MAX_THREADS];
/* Einen neuen TSD-Schlüssel erzeugen - Beim Ende eines
* Threads wird die Funktion thread_close ausgeführt */
pthread_key_create (&tsd_key, thread_close);
/* Threads erzeugen */
for (i = 0; i < MAX_THREADS; ++i)
pthread_create (&(threads[i]), NULL, thread_tsd, NULL);
/* Auf die Threads warten */
for (i = 0; i < MAX_THREADS; ++i)
pthread_join (threads[i], NULL);
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
$ gcc -o thread17 thread17.c -lpthread
$ ./thread17
$ ls *.thread
thread-1209418832.thread thread-1217811536.thread thread-1226204240.thread
$ cat thread-1209418832.thread
Thread wurde gestartet ...
Thread ist fertig ...
|