Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Thread Local Storage

Last updated Jan 1, 2003.

One of the crucial aspects of programming in a multithreaded environment is the ability to define thread-local data. In the June 2008 meeting of the standards committee, the revised proposal to add thread-local storage to C++0x was voted in. The latest version of differs from the original proposal that I described here more than a year ago in two important aspects: the keyword indicating thread-local storage has changed to thread_local, and objects requiring dynamic initializations may have thread-local storage too. In these respects, the C++0x thread-local-storage facility is a significant improvement over the nonstandard __thread facility that many compilers have supported for years.

Background and Existing Practice

Multithreaded applications often need to define data variables that are unique to a thread. Such data variables are said to be thread-local, meaning each thread gets a private copy of the said object instead of sharing a single instance thereof. There are several ways to implement thread-local data. In the POSIX world the getthreadspecific() and setthreadspecific() functions are used for that goal. However, several vendors already offer a special storage class for thread-local variables as a nonstandard extension:

These vendors use the nonstandard keyword (or attribute) __thread to indicate that a certain variable has thread duration (this concept is explained below).

Keyword Naming

In C++0x thread local storage is indicated by the new keyword thread_local. The authors preferred to introduce a new keyword to C++ instead of relying on a library-based solution for two reasons:

  • Speed. Accessing thread local variables is easier and faster when implemented as a core language feature.
  • Ease of migration. It's relatively easier to convert single-thread code to multithreaded code with a core language solution -- you simply have to add the keyword thread_local to the object's declaration.

To my delight, some members of the committee probably heeded my gripe about the original keyword proposed and agreed to replace it with something that looks more self-explanatory -- and more standard C++, I dare say. Here's what I wrote in June 2007 about the originally proposed __thread keyword:

<blockquote>I have some reservation about this naming convention as standard C++ hasn't used keywords preceded with double underscore yet. _Thread (following the precedent of C99 _Bool and C++09 _Char16), or better yet -- thread_local seem better and less likely to collide with existing implementations' non-standard extensions.</blockquote>

Presenting C++0x drafts in the C++ Reference Guide does have some advantages, it appears! Let's look in greater detail at the latest specification of C++0x thread-local storage.

Storage Properties

The proposal adds a new storage duration type called thread duration. Objects with thread duration are thread unique, meaning each thread gets its own copy of the said object. Thread-local objects must have static storage type. More specifically, the following C++98 objects with static duration may be replaced with thread duration objects in a multithreaded environment:

  • Namespace scope objects (e.g., global objects)
  • File-scope static objects
  • Function-local static objects
  • Static data members of a class

Declaring any other types of variable as thread_local is an error:

int f(thread_local int x);//error, arguments have automatic storage type
thread_local register int y; //error, register storage class 

Notice that static and thread_local aren't mutually exclusive. In the following declarations of p, static indicates that the variable isn't visible from other translation units whereas thread_local indicates that every thread gets a private copy of p:

extern thread_local std::string global_s;
int client_request()
{
 thread_local static bool b=false; //function-local static
}
thread_local static void *p=0; //file-scope static
 

Addressing

When the address-of operator & is applied to a thread-local object, that object's address is evaluated at runtime. For example, when dlsym() is used on a thread_local-qualified variable, the address returned will be the address of the currently executing thread's variable. This also means that the address of a thread-local object cannot be a constant expression.

Lifetime, Scope and Visibility

thread_local defines the lifetime and scope of an object. However, it doesn't restrict access to a variable. Therefore, you can pass the address of a thread-local variable to another thread for instance. The address of a thread_local-qualified variable shall remain immutable for the lifetime of the corresponding thread. When the thread terminates, all addresses of its thread-local variables become invalid.

Initialization and Destruction

A thread_local object may be statically initialized as any other static-duration variable. According to the latest specification, dynamic initialization for thread-local objects is also supported. In this respect, the C++0x thread-local facility is superior to the nonstandard __thread storage type that certain implementations offer. Consider:

thread_local std::string s("hello");//dynamic initialization
thread_local int num=4; //static initialization
thread_local int num2=func(); //dynamic initialization
thread_local std::string* ps; //static initialization
thread_local static char buf[200];//static initialization

Generally speaking, compilers that support thread-local storage as a nonstandard extension (see list above) will compile only the declarations that require static initialization whereas compilers supporting the C++0x thread_local keyword will compile all of the declarations above, including those requiring dynamic initialization.