Concurrency in Modern C++ - Hello world

Hello world Concurrency in C++

What is concurrency?

Let’s start off by answering the following question: what is concurrency? Intuitively, concurrency is the execution of operations at the same time. The key part here is at the same time.
Computers are concurrent machines. Nowadays PCs are equipped with several processors and that means that we can exploit all of them at the same time to speed up our software.
Normally an executable runs on a single processor, meaning that at any given time only one of its instructions is executed.
Concurrency is all about executing several instructions at the same time for the same executable.

Hello world code

The C++11 standard introduced a new thread library that allows for standardized programming of concurrent software using threads, but it also offers a bunch of other tools to make concurrent programming safe: synchronization, and atomic operations for instance. Do not worry is any of these words ring a bell now. We will learn a lot about them.

Let’s start whit writing our first concurrent code. The following code spawns a number of threads and each of them will print out a slightly different version of the canonical Hello world message. Each of the threads is assigned a unique identifier and threads personalize the message they print with it.

#include <chrono>
#include <iostream>
#include <string>  //stoul
#include <thread>  // note this header
#include <vector>

using std::cout;
using std::endl;
using std::thread;
using std::vector;
using namespace std::chrono_literals;

void hello_world_function(const unsigned long thread_id) {
    srand(time(0));
  while (1) {
    cout << "Hello from thread number " << thread_id << endl;
    const unsigned int  sleeptime= rand() % 2;
    std::chrono::seconds sec(sleeptime);
    cout << "\tThread " <<thread_id << " is going to sleep for  " << sleeptime << endl;
    std::this_thread::sleep_for(sec);
  }
}

int main(int argc, char* argv[]) {
  cout << "Hello from main thread" << endl;

  const int nthreads = std::stoul(argv[1]);
  vector<thread> threads;
  for (int id = 0; id < nthreads; id++) {
    cout << "Creating thread " << id << endl;
    threads.push_back(
        thread(hello_world_function, id)  // creation of the new thread
    );
  }

  for (int id = 0; id < nthreads; id++) {
    cout << "Joining thread " << id << endl;
    threads[id].join();  // wait for completion of this thread.
  }
  return 0;
}

Let’s compile the code by using the following:

clang++ -O2 -std=c++14 -g -fsanitize=address -Wall  -Wextra -pthreads -o hello_concurrency hello.cpp

Note that we need to provide the -pthreads flag to the compiler. Under the hood, the standard library is using pthreads in Linux.

Code analysis

Let’s analyze the code starting from the mainfunction.

We first take the number of threads we want to create from the command line as an argument. We then create nthreads instances of the std::thread object, and for each of them we will provide two arguments:

  1. the function that this thread will execute
  2. the arguments that this function needs in order to be called.

The thread constructor has the following signature

template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );

As you can see,f is the function that is required for the creation of the thread object, but all the other parameters are optional (we can have an unspecified number of them). If present, they will be passed as arguments to f.

The function that the will execute in this example is a simple one: for 10 times it prints out a message containing the thread id and then it will sleep for a random number of seconds between 0 and 9.
As soon as thread(hello_world_function , id) is executed then the new thread is up and running and it will start executing immediately.
In the meanwhile, the main will continue executing the remaining instruction in the main function.

Now imagine what happens if, hypothetically, the main thread is faster than all the other threads and will reach the end of the main function before the other threads had the chance to execute. The whole process would terminate and so would do all of its associated threads. This is not really what we want (before terminating, we want to have all work done), and so, in order for the main thread to wait until the others have finished, we call join().
In simple terms, joining a thread means that the threads calling join will wait until the joined thread finishes its execution. Whoever calls join on a thread t will be blocked until t completes its execution.

Executing this code is a piece of cake.

[knotman@archazzo] 
→ ./hello_concurrency 3
Hello from main thread
Creating thread 0
Hello from thread number 0
Creating thread 1
Hello from thread number 1
Creating thread 2
Joining thread 0
Hello from thread number Joining thread 1
Joining thread 2

Notice what can happen when we increase the number of threads as in the following (the output from different threads is mixed up). This happens because the OS can pause the execution of one thread to allow another to get some CPU time, and it can be stopped when it is in right in the middle of the cout operation.

[knotman@archazzo] 
→ ./hello_concurrency 3
Hello from main thread
Creating thread 0
Hello from thread number 0
    Thread 0 is going to sleep for  0
Hello from thread number 0
    Thread 0 is going to sleep for  0
Hello from thread number 0
    Thread 0 is going to sleep for  0
Hello from thread number 0
    Thread 0 is going to sleep for  1
Creating thread 1
Hello from thread number 1
    Thread 1 is going to sleep for  0
Hello from thread number 1
    Thread 1 is going to sleep for  0
Hello from thread number 1
    Thread 1 is going to sleep for  0
Hello from thread number 1
    Thread 1 is going to sleep for  1
Creating thread 2
Creating thread 3
Hello from thread number 2
    Thread 2 is going to sleep for  0
Hello from thread number 2
    Thread 2 is going to sleep for  0
Hello from thread number 2
    Thread 2 is going to sleep for  0
Hello from thread number 2
    Thread 2 is going to sleep for  1
Creating thread 4
Hello from thread number 3
    Thread 3 is going to sleep for  0
Hello from thread number 3
    Thread 3 is going to sleep for  0
Hello from thread number 3
    Thread 3 is going to sleep for  0
Hello from thread number 3
    Thread 3 is going to sleep for  1
Hello from thread number 4
    Thread 4 is going to sleep for  0
Hello from thread number 4
    Thread 4 is going to sleep for  0
Hello from thread number 4
    Thread 4 is going to sleep for  0
Hello from thread number 4
    Thread 4 is going to sleep for  1
Creating thread 5
Creating thread 6
Hello from thread number 5
    Thread 5 is going to sleep for  0
Hello from thread number 5
    Thread 5 is going to sleep for  0
Hello from thread number 5
    Thread 5 is going to sleep for  0
Hello from thread number 5
    Thread 5 is going to sleep for  1
Creating thread 7
Hello from thread number 6
    Thread 6 is going to sleep for  0
Hello from thread number 6
    Thread 6 is going to sleep for  0
Hello from thread number 6
    Thread 6 is going to sleep for  0
Hello from thread number 6
    Thread 6 is going to sleep for  1
Creating thread 8
Hello from thread number 7
    Thread 7 is going to sleep for  0
Hello from thread number 7
    Thread 7 is going to sleep for  0
Hello from thread number 7
    Thread 7 is going to sleep for  0
Hello from thread number 7
    Thread 7 is going to sleep for  1
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  1
Creating thread 9
Joining thread 0
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  1
Hello from thread number 0
    Thread 0 is going to sleep for  1
Hello from thread number 1
Hello from thread number    Thread 2
1   Thread 2 is going to sleep for  1
 is going to sleep for  1
Hello from thread number 3
    Thread 3 is going to sleep for  1
Hello from thread number 4
    Thread 4 is going to sleep for  0
Hello from thread number 4
    Thread 4 is going to sleep for  1
Hello from thread number 5
    Thread 5 is going to sleep for  0
Hello from thread number 5
    Thread 5 is going to sleep for  1
Hello from thread number 6
    Thread 6 is going to sleep for  1
Hello from thread number 7
    Thread 7 is going to sleep for  0
Hello from thread number 7
    Thread 7 is going to sleep for  1
Hello from thread number 8
    Thread 8 is going to sleep for  1
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  1
Hello from thread number 0
    Thread 0 is going to sleep for  1
Hello from thread number 2
    Thread 2 is going to sleep for  1
Hello from thread number 1
    Thread 1 is going to sleep for  1
Hello from thread number 3
    Thread 3 is going to sleep for  1
Hello from thread number 4
    Thread 4 is going to sleep for  1
Hello from thread number 5
    Thread 5 is going to sleep for  1
Hello from thread number 6
    Thread 6 is going to sleep for  0
Hello from thread number 6
    Thread 6 is going to sleep for  1
Hello from thread number 7
    Thread 7 is going to sleep for  1
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  1
Hello from thread number 9
    Thread 9 is going to sleep for  1
Hello from thread number 0
Hello from thread number    Thread 0 is going to sleep for  0
Hello from thread number 0
    Thread 0 is going to sleep for  1
2
    Thread 2Hello from thread number  is going to sleep for  0
Hello from thread number 2
    Thread 2 is going to sleep for  0
1
    Thread 1 is going to sleep for  Hello from thread number 03

Hello from thread number Hello from thread number 1
    Thread  Thread 31 is going to sleep for   is going to sleep for  1
1
2
    Thread 2 is going to sleep for  1
Hello from thread number 5
    Thread 5 is going to sleep for  0
Hello from thread number 5
    Thread 5 is going to sleep for  1
Hello from thread number 6
    Thread 6 is going to sleep for  0
Hello from thread number 6
    Thread 6 is going to sleep for  1
Hello from thread number 4
    Thread 4 is going to sleep for  1
Hello from thread number 7
    Thread 7 is going to sleep for  1
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 8
    Thread 8 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  0
Hello from thread number 9
    Thread 9 is going to sleep for  1
Hello from thread number Hello from thread number Hello from thread number 3Hello from thread number 
2Hello from thread number   Thread 3 is going to sleep for  51
0
1   Thread 2 is going to sleep for  1

Hello from thread number 
    Thread 5 is going to sleep for  1

6   Thread 1 is going to sleep for  1
Hello from thread number 4
    Thread 4 is going to sleep for  1
    Thread 0 is going to sleep for  0
Hello from thread number 0
    Thread 0 is going to sleep for  1
Hello from thread number 7
    Thread 7 is going to sleep for  0

Hello from thread number 7
    Thread 7 is going to sleep for  0
    Thread 6 is going to sleep for  1
Hello from thread number Hello from thread number 3
    Thread 34
Hello from thread number 1
    Thread 1 is going to sleep for  0
    Thread 4 is going to sleep for  1
 is going to sleep for  1
Joining thread 1
Joining thread 2
Joining thread 3
Hello from thread number 3
    Thread 3 is going to sleep for  1
Joining thread 4
Joining thread 5
Joining thread 6
Joining thread 7
Joining thread 8
Joining thread 9

Summary

We have seen how to create and start running multiple threads. We have also learned how to make a thread wait ( join() ) for another thread before it is allowed to continue its execution.  You should now be able to write, compile and execute simple multi-threaded code.

References

cppreference.com, std::thread

One Comment

  • Many thanks for sharing such great information, as a web developer this isreally helpful for me.

Join the Discussion

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>