Professional C__ - Marc Gregoire [388]
Thread with Function Pointer
Functions like CreateThread(), _beginthread(), and so on, on Windows, and pthread_create() with the pthreads library, require that the thread function only has one parameter. On the other hand, a function that you want to use with the C++11 std::thread class can have as many parameters as you want.
Suppose you have the following function. The counter() function accepts two integers: the first representing an ID and the second representing the number of iterations that the function should loop. The body of the function is a single loop that loops the given number of iterations. On each iteration, a message is printed to standard output:
void counter(int id, int numIterations)
{
for (int i = 0; i < numIterations; ++i) {
cout << "Counter " << id << " has value ";
cout << i << endl;
}
}
Code snippet from thread\ThreadFunction.cpp
You can launch multiple threads executing this function using std::thread. You can create a thread t1, executing counter() with arguments 1 and 6 as follows:
thread t1(counter, 1, 6);
The constructor of the thread class is a variadic template, which means that it accepts any number of arguments. Variadic templates are discussed in detail in Chapter 20. The first argument is the name of the function to execute in the new thread. The subsequent variable number of arguments are passed to this function when execution of the thread starts.
If you want to run the counter() function in parallel in several threads, you need to make sure that access to cout is thread-safe. To make this thread-safe, you need to call:
cout.sync_with_stdio(true);
Without calling sync_with_stdio(true), accessing the stream from multiple threads might cause race conditions.
The following code launches two threads executing the counter() function. After launching the threads, main() calls join() on both threads. This is to make sure that the main thread keeps running until both threads are finished. A call to t1.join() blocks until the thread t1 is finished. Without these two join() calls, the main() function would finish immediately after launching the two threads. This will trigger the application to shut down; causing all other threads spawned by the application to be terminated as well, whether these threads are finished or not.
These join() calls are necessary in these small examples. In real-world applications, you should try to avoid the use of join(), because it causes the thread calling join() to block. Often there are better ways. For example, in a GUI application, a thread that finishes can post a message to the UI thread. The UI thread itself has a message loop processing messages like mouse moves, button clicks and so on. This message loop can also receive the messages from the thread, and you can react to them how you want, all without blocking the UI thread with a join() call.
#include #include using namespace std; int main() { cout.sync_with_stdio(true); // Make sure cout is thread-safe thread t1(counter, 1, 6); thread t2(counter, 2, 4); t1.join(); t2.join(); return 0; } Code snippet from thread\ThreadFunction.cpp A possible output of this example looks as follows: Counter 2 has value 0 Counter 1 has value 0 Counter 1 has value 1 Counter 1 has value 2 Counter 1 has value 3 Counter 1 has value 4 Counter 1 has value 5 Counter 2 has value 1 Counter 2 has value 2 Counter 2 has value 3 The output on your system will be different and it will most likely be different every time you run it. This is because two threads are executing the counter() function at the same time, so the output depends on the number of processing cores in your system and on the thread scheduling of the operating system. Since this example is calling cout.sync_with_stdio(true), accessing cout is thread-safe, however, output from different threads can still be interleaved. This means that