ntil not long ago, a standardized multithreading API for C++ was a pipe dream. Almost every operating system, compiler, and framework rolled their own threading libraries. These proprietary libraries were complex, non-portable, and didn’t support object-oriented idioms such as function objects. In the following sections, you’ll learn how to create threads and mutexes in the C++0x fashion.
As multicore processors become predominant, you want to write multithreaded C++ code that’s both efficient and portable.
Migrate to the new C++0x threading libraries.
Hang By a Thread
At the heart of the new C++0x threading library lies std::thread, which provides mechanisms for creating a new thread of execution, waiting for a thread to complete (this is know as a joining with a thread), and querying the state of a thread. You launch a new thread by instantiating a std::threadobject initialized with a function:
#include void spellcheck();std::thread thr(spellcheck);
The preceding code invokes spellcheck() when it creates thr. When spellcheck() returns the thread is finished. std::thread can take any callable entity as its argument, not just a plain old function. The following code passes a function objectas an argument:
class spellcheck{ void operator()();};spellcheck spstd::thread thr(sp);
thr actually gets a copy of sp. If you don’t want to create copies, wrap the arguments in a reference wrapper:
spellcheck sp;std::thread thr(std::ref(sp));
This is all well and good, but how do you pass arguments to the thread function itself? In traditional threading frameworks, you’d typically pack parameters in an opaque pointer. Fortunately, those days are gone. std::threaduses another new C++ feature called variadic templates, which allows you to pass any number of arguments to the thread function:
void spellcheck(CDocument *p, string slocale, bool igncase);std::thread thr(spellcheck, pd, "en_UK", true);
All the examples above launch a new thread upon initialization. In some cases you want to create an “empty” thread—one that does not represent a thread of execution. Although copying threads isn’t allowed (and doesn’t make any sense either), you can movean active thread into an empty thread like this:
std::thread thr(spellcheck,pd,"en_UK",true);std::thread thr2; thr2=std::move(thr);
Or simply call swap():
std::swap(thr,thr2);
Join and Detach
After launching a thread, your process can join with the thread. To do that, call the join()member function:
std::thread thr(spellcheck);thr.join();
Alternatively, if you don’t want to join with the thread, you can destroy the thread by calling detach():
std::thread thr(spellcheck);thr.detach();
Each thread is assigned a unique id that you can use for comparing, sorting, and storing threads in an associative container. To obtain the id of an existing thread, call get_id():
thread::id sp_id;sp_id=thr.get_id();
Data Protection
A mutex provides exclusive ownership for a thread. The
- std::mutex
- std::recursive_mutex
- std::timed_mutex
- std::recursive_timed_mutex
All mutexes provide member functions for locking and unlocking a mutex. However, the more common approach is to use the std::unique_lock and std::lock_guard class templates. These templates implement the RAII idiom, locking the resource upon initialization and releasing it when they are destroyed. In the following example, lock_guard is used to gain exclusive access to s:
std::mutex mtx;std::string s;void f(){ std::lock_guard lck(mtx); all_caps(s);} // the mutex is released here
If you’re looking for a more sophisticated locking mechanism that supports deferred locking and timeouts, use unique_lock instead. The following code attempts to lock a resource with a timeout. The locking operation must happen in 10 milliseconds. Otherwise, the function f()will give up and exit:
#include //a new C++0x library#include std::timed_mutex mtx;std::string s;void f(){std::unique_lock lck(mtx,std::chrono::milliseconds(10));//wait up to 10ms if(lck) // did the lock succeed? all_caps(s);} // the mutex is released here
Pick Up the Threads
The examples shown here are only an appetizer. The new threading library offers much more than that. For instance, std::condition_variable lets a thread sleep until it has been notified by another thread. Additional facilities for ensuring the proper initialization of objects with static storage duration are also provided, as are deferred locks, and thread-localobjects.