Multithreading in C++
Multithreading is a specialized form of multitasking that allows a computer to run two or more programs concurrently. Generally, there are two types of multitasking: process-based and thread-based.
- Process-based multitasking involves the concurrent execution of programs.
- Thread-based multitasking involves the concurrent execution of segments within the same program.
A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate execution path.
This tutorial assumes you are using a Linux operating system and will use POSIX to write multithreaded C++ programs. The POSIX Threads or Pthreads provided API is available on various Unix-like POSIX systems such as FreeBSD, NetBSD, GNU/Linux, Mac OS X, and Solaris.
Creating Threads
The following program can be used to create a POSIX thread:
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
Here, pthread_create creates a new thread and makes it executable. Below are the descriptions of the parameters:
Parameter | Description |
---|---|
thread | A pointer to the thread identifier. |
attr | An opaque attribute object that can be used to set thread attributes. You can specify a thread attribute object, or use the default value NULL. |
start_routine | The starting address of the thread function to be executed once the thread is created. |
arg | The arguments to the thread function. It must be passed by casting the reference to void type. If no arguments are passed, use NULL. |
The function returns 0 upon successful thread creation, and a non-zero value indicates a failure to create the thread.
Terminating Threads
The following program can be used to terminate a POSIX thread:
#include <pthread.h>
pthread_exit (status)
Here, pthreadexit is used to explicitly exit a thread. Typically, the pthreadexit() function is called when the thread has completed its work and does not need to exist anymore.
If main() finishes before the threads it created and exits using pthread_exit(), the other threads will continue to execute. Otherwise, they will be automatically terminated when main() finishes.
Example
The following simple example code uses the pthread_create() function to create 5 threads, each outputting "Hello tutorialpro!":
#include <iostream>
// Required header file
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
// Thread function
void* say_hello(void* args)
{
cout << "Hello tutorialpro!" << endl;
return 0;
}
int main()
{
// Define thread id variable, multiple variables use an array
pthread_t tids[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; ++i)
{
// Parameters are: the created thread id, thread parameters, the function to call, function parameters
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
// Wait for all threads to exit before the process ends, otherwise the process may forcefully end before the threads have a chance to finish.
pthread_exit(NULL);
}
Compile the following program using the -lpthread library:
$ g++ test.cpp -lpthread -o test.o
Now, execute the program, which will produce the following result:
$ ./test.o
Hello tutorialpro!
Hello tutorialpro!
Hello tutorialpro!
Hello tutorialpro!
Hello tutorialpro!
The following simple example code uses the pthread_create() function to create 5 threads and receives the input parameters. Each thread prints a "Hello tutorialpro!" message and outputs the received parameter, then calls pthread_exit() to terminate the thread.
// File name: test.cpp
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid)
{
// Cast the passed parameter from void pointer to integer pointer, then dereference it
int tid = *((int*)threadid);
cout << "Hello tutorialpro! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
int indexes[NUM_THREADS]; // Array to store the values of i
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : Creating thread, " << i << endl;
indexes[i] = i; // Save the value of i first
// Must cast to void* when passing parameters
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&(indexes[i]));
if (rc){
cout << "Error: Unable to create thread, " << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
When compiling and executing the program, the following result is produced:
$ g++ test.cpp -lpthread -o test.o
$ ./test.o
main() : Creating thread, 0
main() : Creating thread, 1
Hello tutorialpro! Thread ID, 0
main() : Creating thread, Hello tutorialpro! Thread ID, 21
main() : Creating thread, 3
Hello tutorialpro! Thread ID, 2
main() : Creating thread, 4
Hello tutorialpro! Thread ID, 3
Hello tutorialpro! Thread ID, 4
Passing Parameters to Threads
This example demonstrates how to pass multiple parameters through a structure. You can pass any data type in the thread callback because it points to void, as shown in the following example:
Example
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data{
int thread_id;
char *message;
};
void *PrintHello(void *threadarg)
{
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main ()
{
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;
for( i=0; i < NUM_THREADS; i++ ){
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = (char*)"This is message";
rc = pthread_create(&threads[i], NULL,
PrintHello, (void *)&td[i]);
if (rc){
cout << "Error: unable to create thread, " << rc << endl;
When the above code is compiled and executed, it produces the following result:
$ g++ -Wno-write-strings test.cpp -lpthread -o test.o
$ ./test.o
main() : creating thread, 0
main() : creating thread, 1
Thread ID : 0 Message : This is message
main() : creating thread, 2
Thread ID : 1 Message : This is message
main() : creating thread, 3
Thread ID : 2 Message : This is message
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 4 Message : This is message
Joining and Detaching Threads
We can use the following two functions to join or detach threads:
pthread_join (threadid, status)
pthread_detach (threadid)
The pthread_join() subroutine blocks the calling program until the specified threadid thread terminates. When a thread is created, one of its attributes defines whether it is joinable or detached. Only threads that are created as joinable can be joined. If a thread is created as detached, it can never be joined.
This example demonstrates how to use the pthread_join() function to wait for a thread to finish.
Example
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t)
{
int i;
long tid;
tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main ()
{
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// Initialize and set thread joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for( i=0; i < NUM_THREADS; i++ ){
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, wait, (void *)&i );
if (rc){
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
// Free attribute and wait for the other threads
pthread_attr_destroy(&attr);
for( i=0; i < NUM_THREADS; i++ ){
rc = pthread_join(threads[i], &status);
if (rc){
cout << "Error:unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}
cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}
When the above code is compiled and executed, it produces the following result:
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 4 ...exiting
Sleeping in thread
Thread with id : 3 ...exiting
Sleeping in thread
Thread with id : 2 ...exiting
Sleeping in thread
Thread with id : 1 ...exiting
Sleeping in thread
Thread with id : 0 ...exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.
std::thread
After C++ 11, a new standard thread library std::thread
was added. std::thread
is declared in the <thread>
header file, so you need to include the <thread>
header file when using std::thread
.
Some compilers used the -std=c++11 compilation flag for C++ 11:
g++ -std=c++11 test.cpp
The default constructor of std::thread
creates an empty std::thread execution object.
#include<thread>
std::thread thread_object(callable)
A callable object can be any of the following three:
Function pointer
Function object
Lambda expression
After defining the callable, pass it to the std::thread
constructor thread_object.
Example
// CPP program to demonstrate multithreading
// using three different callables.
#include <iostream>
#include <thread>
using namespace std;
// A dummy function
void foo(int Z)
{
for (int i = 0; i < Z; i++) {
cout << "Thread using function pointer as callable\n";
}
}
// A callable object
class thread_obj {
public:
void operator()(int x)
{
for (int i = 0; i < x; i++)
cout << "Thread using function object as callable\n";
}
};
int main()
{
cout << "Threads 1, 2, 3 "
"operating independently" << endl;
// Function pointer
thread th1(foo, 3);
// Function object
thread th2(thread_obj(), 3);
// Lambda expression
auto f = [](int x) {
for (int i = 0; i < x; i++)
cout << "Thread using lambda expression as callable\n";
};
// Thread using lambda expression as callable
thread th3(f, 3);
// Wait for the threads to finish
// Wait for thread t1 to finish
th1.join();
// Wait for thread t2 to finish
th2.join();
// Wait for thread t3 to finish
th3.join();
return 0;
}
Compile with the C++ 11 flag -std=c++11:
g++ -std=c++11 test.cpp
When the above code is compiled and executed, it produces the following result: Threads 1, 2, 3 run independently. Threads use function pointers as callable parameters. Threads use function pointers as callable parameters. Threads use function pointers as callable parameters. Threads use function objects as callable parameters. Threads use function objects as callable parameters. Threads use function objects as callable parameters. Threads use lambda expressions as callable parameters. Threads use lambda expressions as callable parameters. Threads use lambda expressions as callable parameters.
For more examples, see:
C++ Multithreading: http://www.tutorialpro.org/w3cnote/cpp-multithread-demo.html
C++ std::thread: https://www.tutorialpro.org/w3cnote/cpp-std-thread.html