c11 Threads Paper
c11 Threads Paper
to C++11 threads
for users of pthreads
Marc Paterno
Fermilab/SCD/ADSS/SSI
Contents
1 Major differences between pthreads and C++11 1
2 An example use of C++11 threads 2
3 Futures 4
4 What I could not cover in 12 minutes 5
5 What’s missing in GCC 4.7.1 5
6 References 5
1. pthreads is a C library, and was not designed with some issues critical to C++ in
mind, most importantly object lifetimes and exceptions.
2. pthreads provides the function pthread_cancel to cancel a thread. C++11 provides
no equivalent to this.
3. pthreads provides control over the size of the stack of created threads; C++11 does
not address this issue.1
4. C++11 provides the class thread as an abstraction for a thread of execution.
5. C++11 provides several classes and class templates for mutexes, condition variables,
and locks, intending RAII2 to be used for their management.
6. C++11 provides a sophisticated set of function and class templates to create callable
objects and anonymous functions (lambda expressions) which are integrated into
the thread facilities.
1
On Linux, setrlimit is available, but affects all threads in the process.
2
Resource allocation is initialization.
1
2 C++11 threads (Rev. 1)
The use of RAII to control thread resources (including mutexes) cannot be over-emphasized.
RAII is at the center of the design of the C++11 thread library and all of its facilities.
9 t1.join();
10 t2.join();
11 }
Listing 2.1: The main program.
The call to std::thread::join() stalls the calling (main) thread-of-execution until the
thread on which it is called finishes.
14 }
15 Workspace w(limit); // To be used by GSL’s QAGS
16 double result(0.0), error(0.0);
17 gsl_function func {f, 0}; // struct defined by GSL
18 gsl_integration_qags(&func, low, high, abs, 0.0, limit,
19 w.get(), &result, &error);
21 std::lock_guard<std::mutex> lck(G_COUT_MUTEX);
22 std::cout << "In thread: " << std::this_thread::get_id()
23 << " result: " << result
24 << " error: " << error
25 << " intervals: " << w.size()
26 << std::endl;
27 }
Listing 2.2: The thread function.
Note how the lifetimes of objects are used to control the acquisition and release of the
mutex. C++ strictly defines the lifetimes of created objects; rely on them!
Note also GSL’s technique for obtaining thread safety without locks: pass to a function
all the data it uses, rather than using static data.
13 Workspace::Workspace(std::size_t lim) :
14 ws_(gsl_integration_workspace_alloc(lim)) { }
15 Workspace::˜Workspace() { gsl_integration_workspace_free(ws_); }
16 std::size_t Workspace::size() const { return ws_->size; }
17 gsl_integration_workspace* Workspace::get() { return ws_; }
Listing 2.3: The sentry class Workspace.
4 C++11 threads (Rev. 1)
The unnamed void* second argument is forced upon us by the design of GSL; it is
ignored.
On Linux, you may also need to include -lgslcblas -lpthread; the requirement to
name -lpthread appears to me to be a bug in GCC 4.7.1.
The result of execution is (extra line breaks added to fit on this page):
Starting integration in thread 0x100581000
Starting integration in thread 0x100781000
In thread: 0x100781000 result: 310.394 error: 9.83391e-12
intervals: 9
In thread: 0x100581000 result: 0.0199936 error: 9.99855e-12
intervals: 16346
3 Futures
The class std::future can be used to encapsulate a function run in its own thread of
execution, and to obtain its return value (or an exception it throws). The function template
std::async is used to create the future; the enumeration values std::launch::async
and std::launch::deferred determine when the thread-of-execution begins.
1 #include <future>
2 #include <iostream>
3 #include <string>
8 int main() {
9 std::future<int> a = std::async(std::launch::deferred, f);
C++11 threads (Rev. 1) 5
There are many other things of interest for multithreaded programming C++11. Some of
them are:
• Additional mutex and lock types, and locking strategies, e.g., std::recursive_mutex,
std::timed_mutex; std::unique_lock; std::defer_lock, std::try_to_lock.
• Condition variables (some uses of POSIX condition variables are better replaced by
std::future).
• Class templates duration and time_point, used in all time-related interfaces, e.g.,
std::this_thread::sleep_for and std::this_thread_sleep_until.
• Atomic types and functions on atomic types.
• Memory fence functions to for memory-ordering between operations.
• Variadic templates (which enable the simple means of passing arguments to a thread
function)
• Lambda expressions (anonymous closure objects), which can be used in place of
functions.
• rvalue references, which enable perfect forwarding to a thread function
• Additional support for function objects, e.g., from std::function and std::bind.
GCC 4.7.1, when used with the -std=c++11 flag, support much but not all of C++11. The
full feature matrix is available at http://gcc.gnu.org/gcc-4.7/cxx0x_status.html.
The important items related to concurrency are:
• No support of thread-local storage.
• Very limited support for atomics.
• The new memory model is not yet implemented.
To access the std::this_thread::sleep_until and std::this_thread::sleep_for
functions requires using -D_GLIBCXX_USE_NANOSLEEP on some platforms.
6 References
There are many online references available. Ones I have used include:
6 C++11 threads (Rev. 1)
• http://en.cppreference.com/w/cpp/thread.
• The C++ committee public web site: http://www.open-std.org/jtc1/sc22/wg21.
My favorite book on the subject is C++ Concurrency in Action, by Anthony Williams,
ISBN 1933988770.