CPP Multithreading

Understanding Multithreading in C++ Programming Language: Concurrent Execution and Thread Management

Multithreading is a specialized form of multitasking, a feature that enables your computer to run multiple programs simultaneously. In general, multitasking is divided into two types:

process-based and thread-based.

Process-based multitasking handles concurrent program execution, while thread-based multitasking manages concurrent execution of segments within the same program. Multithreading in C++ provides the ability to execute multiple threads within a single program, enabling parallelism and improved performance in handling concurrent tasks.

In a multithreaded program, two or more sections can run concurrently. Each segment, known as a thread, defines an independent execution path.

Prior to C++11, there was no built-in support for multithreaded applications, relying solely on the operating system for this feature.

This tutorial assumes you’re on a Linux OS and will guide you through creating a multi-threaded C++ program using POSIX threads. POSIX Threads (Pthreads) provide an API available on Unix-like POSIX systems such as Linux, macOS, and others.

Creating Threads

The following function creates a POSIX thread:

#include <pthread.h>
pthread_create(thread, attr, start_routine, arg)
  • pthread_create generates a new thread, making it executable.
  • It can be called multiple times from anywhere in your code.
  • Parameters:
  • thread: A unique identifier for the new thread returned by the function.
  • attr: An attribute object used to set thread attributes (NULL for defaults).
  • start_routine: The function the thread will execute upon creation.
  • arg: An argument passed to start_routine.

Note: The maximum threads a process can create is system-dependent. Threads are peers without implied hierarchy; one thread can create others.

Terminating Threads

To explicitly exit a thread, use the pthread_exit() function:

#include <pthread.h>
pthread_exit(status)
  • Call pthread_exit() after a thread completes its work and isn’t needed.
  • If main() exits before created threads, using pthread_exit() lets those threads continue.
  • Threads created as joinable can be explicitly terminated with pthread_join().
  • Detached threads can’t be joined and are automatically terminated when main() finishes.

Example: Creating Threads

This code creates five threads using pthread_create(), with each thread printing “Hello World!” and terminating via pthread_exit():

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

void *PrintHello(void *threadid) {
   long tid;
   tid = (long)threadid;
   cout << "Hello World! Thread ID: " << tid << endl;
   pthread_exit(NULL);
}

int main () {
   pthread_t threads[NUM_THREADS];
   int rc;
   int i;

   for (i = 0; i < NUM_THREADS; i++) {
      cout << "main(): Creating thread " << i << endl;
      rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);

      if (rc) {
         cout << "Error: Unable to create thread, " << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Passing Arguments to Threads

You can pass multiple arguments using a structure. This example uses a structure to pass arguments to threads:

#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 = "This is a message";
      rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);

      if (rc) {
         cout << "Error: Unable to create thread, " << rc << endl;
         exit(-1);
      }
   }
   pthread_exit(NULL);
}

Joining and Detaching Threads

Use pthread_join() to block the calling thread until the specified thread terminates:

#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;

   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], &attr, wait, (void *)i);
      if (rc) {
         cout << "Error: Unable to create thread, " << rc << endl;
         exit(-1);
      }
   }

   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);
}

Discover more from PiEmbSysTech

Subscribe to get the latest posts sent to your email.

Leave a Reply

Scroll to Top

Discover more from PiEmbSysTech

Subscribe now to keep reading and get access to the full archive.

Continue reading