Die Boost C++ Bibliotheken

Thread-spezifischer Speicher

Thread-spezifischer Speicher – auf Englisch thread local storage (TLS) – ist ein Speicherbereich, auf den jeweils nur ein Thread Zugriff hat. Sie können sich TLS-Variablen als globale Variablen vorstellen, die jedoch nicht global im gesamten Prozess sind, sondern lediglich in einem Thread. Welchen Nutzen derartige Variablen haben, soll Ihnen anhand des folgenden Beispiels gezeigt werden.

Beispiel 44.12. Synchronisation mehrerer Threads mit einer statischen Variablen
#include <boost/thread.hpp>
#include <iostream>

boost::mutex mutex;

void init()
{
  static bool done = false;
  boost::lock_guard<boost::mutex> lock{mutex};
  if (!done)
  {
    done = true;
    std::cout << "done" << '\n';
  }
}

void thread()
{
  init();
  init();
}

int main()
{
  boost::thread t[3];

  for (int i = 0; i < 3; ++i)
    t[i] = boost::thread{thread};

  for (int i = 0; i < 3; ++i)
    t[i].join();
}

Beispiel 44.12 führt die Funktion thread() in drei Threads aus. thread() ruft eine andere Funktionen init() zweimal auf. init() überprüft, ob die Variable done vom Typ bool false ist. Ist sie das, wird sie auf true gesetzt und done auf die Standardausgabe ausgegeben.

Da done eine statische Variable ist, wird sie von allen Threads geteilt. Wenn done im ersten Thread auf true gesetzt wird, wird der zweite und dritte Thread kein done auf die Standardausgabe ausgeben. Der zweite Aufruf von init() in thread() führt ebenfalls nicht zu einer Ausgabe von done. Beispiel 44.12 gibt done genau einmal auf die Standardausgabe aus.

Eine statische Variable wie done kann verwendet werden, um eine einmalige Initialisierung vorzunehmen – einmal in einem Prozess und unabhängig davon, wie viele Threads verwendet werden. Soll eine einmalige Initialisierung pro Thread stattfinden, können TLS-Variablen verwendet werden.

Beispiel 44.13. Synchronisation mehrerer Threads mit TLS-Variablen
#include <boost/thread.hpp>
#include <iostream>

boost::mutex mutex;

void init()
{
  static boost::thread_specific_ptr<bool> tls;
  if (!tls.get())
  {
    tls.reset(new bool{true});
    boost::lock_guard<boost::mutex> lock{mutex};
    std::cout << "done" << '\n';
  }
}

void thread()
{
  init();
  init();
}

int main()
{
  boost::thread t[3];

  for (int i = 0; i < 3; ++i)
    t[i] = boost::thread{thread};

  for (int i = 0; i < 3; ++i)
    t[i].join();
}

Im Beispiel 44.13 wurde die statische Variable done durch eine TLS-Variable tls ersetzt. Sie basiert auf der Template-Klasse boost::thread_specific_ptr, die mit dem Typ bool instanziiert ist. Die neue Variable tls funktioniert grundsätzlich genauso wie done: Es handelt sich um einen Schalter, mit dem kontrolliert wird, ob etwas bereits erledigt wurde. Der entscheidende Unterschied ist, dass der Wert, den tls speichert, nur im jeweiligen Thread verfügbar ist. Auch wenn die statische Variable tls nur einmal existiert, so existiert ein in tls gespeicherter Wert nur im jeweiligen Thread und ist für andere Threads nicht sichtbar.

Nachdem eine Variable vom Typ boost::thread_specific_ptr erstellt wurde, kann sie gesetzt werden. Die Klasse boost::thread_specific_ptr erwartet jedoch keine bool-Variable, sondern die Adresse einer bool-Variablen. Indem die Methode reset() aufgerufen wird, kann die Adresse einer bool-Variablen in tls gespeichert werden. Im Beispiel 44.13 wird eine bool-Variable dynamisch reserviert und die Adresse, die von new zurückgegeben wird, in tls gespeichert. Damit dies nur einmal geschieht, wird vorher mit get() überprüft, ob bereits eine Adresse in der Variablen tls gespeichert ist.

Da boost::thread_specific_ptr eine Adresse speichert, verhält sich diese Klasse wie ein Zeiger. So stehen zum Beispiel die Operatoren operator* und operator-> zur Verfügung, die genauso funktionieren wie von Zeigern gewohnt.

Beispiel 44.13 gibt done dreimal auf die Standardausgabe aus. Jeder Thread gibt done einmal aus, und zwar jeweils im ersten Aufruf von init(). Weil das Beispiel eine TLS-Variable verwendet, besitzt jeder Thread seinen eigenen Schalter. Wenn der erste Thread die Variable tls mit einem Zeiger auf eine dynamisch reservierte bool-Variable initialisiert, ist tls in den anderen beiden Threads noch nicht initialisiert. Weil TLS-Variablen nicht global im gesamten Prozess, sondern nur global in einem Thread sind, hat eine Änderung der Variablen tls in einem Thread keine Auswirkungen auf andere Threads.