Hello World of concurrency (using std::thread)
Now, let's get started with our first program using the std::thread library. You are expected to have C++ 11 or later to compile the programs we are going to discuss in this chapter. Let's take a simple, classic Hello World example as a reference before going into a multi-threaded Hello World:
//---- Thanks to Dennis Ritchie and Brian Kernighan, this is a norm for all languages
#include <iostream> int main() { std::cout << "Hello World\n"; }
This program simply writes Hello World into the standard output stream (mainly the console). Now, let's see another example that does the same stuff, but using a background thread (often called a worker thread instead):
#include <iostream> #include <thread> #include <string> //---- The following function will be invoked by the thread library void thread_proc(std::string msg) { std::cout << "ThreadProc msg:" << msg; } int main() { // creates a new thread and execute thread_proc on it. std::thread t(thread_proc, "Hello World\n"); // Waiting for the thread_proc to complete its execution // before exiting from the program t.join(); }
The first difference with traditional code is the inclusion of the <thread> standard header file. All of the multithreading support functions and classes are declared in this new header. But to achieve synchronization and shared data protection, the supporting classes are available in other headers. If you are familiar with platform-level threads in Windows or POSIX systems, all threads require an initial function. The same concept is what the standard library is also following. In this example, the thread_proc function is the initial function of a thread that's declared in the main function. The initial function (through the function pointer) is specified in the constructor of the std::thread object t, and construction starts the execution of the thread.
The most notable difference is that now the application writes the message into a standard output stream from a new thread (background thread), which results in having two threads or a path of execution in this application. Once the new thread has been launched, the main thread continues its execution. If the main thread is not waiting for the newly started thread to finish, the main() function would end and thus that would be the end of the application—even before the new thread has had the chance to finish its execution. This is the reason for calling join() before the main thread finishes, in order to wait for the new thread, t, which is started here.