Tuesday, February 20, 2024

MULTITHREADING AND MEMORY MANAGEMENT IN C++

 Multithreading

  • WHAT IS MULTITHREADING
  • ITS PARAMETER AND DESCRIPTION
  • TERMINATION THREADS
  • PASSING ARGUMENT TO THREAD
  • JOINING AND DETACHING THREAD
What is memory management?
    • WHAT IS MEMORY MANAGEMENT 
    • WHY IS IT REQUIRED

Memory management is the process of managing computer memory and allocating memory space to programs to improve overall system performance.

 Why is It required?

 Since we know that arrays store homogeneous data, in most cases, memory is allocated for the array during its declaration. Sometimes a situation occurs when the exact memory becomes clear only while driving. To avoid this situation, we define the table with the largest size, but some memory remains unused. To avoid wasting memory, we use a new operator to dynamically allocate memory at runtime.

 Memory management operators: -

 We use malloc() or calloc () functions to dynamically allocate memory at runtime and free() to allocate dynamically allocated memory. C++ also supports these functions, but C++ also defines non-privileged operators such as new and delete to perform the same tasks, ie. allocate and free memory.


WHAT IS MULTITHREADING

Multitasking is a special form of multitasking, it has a feature that allows a computer to run two or more programs at the same time. Normally multitasking can be divided into two parts: process-based and thread-based.
In process-based multitasking, we deal with concurrent execution of programs. On the other hand in thread-based multitasking, we simultaneously execute the parts of the same program.

In a multithreaded program, we can see that two or more parts of the program run simultaneously. Each part that is running is called a thread, and also each thread has its own separate path of execution.

The first time C++11 introduced the multi-threading before it, it was not present in the C++ environment, and C++ was solely dependent on the operating system for multi-threading.  

In our blog, we assume that you are using the Linux operating system, and therefore we are writing a multithreaded C++ program using POSIX. The POSIX threads or Pthreads have an API, this API is available on many Unix-like POSIX systems like FreeBSD, NetBSD, GNU/Linux, Max OS X, and Solaris. 

Creating threads

 The following code is used to create POSIX thread: -

 #include <pthread.h>

pthread_create (thread, attr, start_routine, arg) 
 
Here, pthread_create creates a new thread and makes it executable. This routine can be called multiple times in your code. Here is a description of the parameters.
 

Sr.No

Parameter & Description

1

thread

An opaque, unique identifier for the new thread returned by the subroutine.

2

attr

An opaque attribute object that may be used to set thread attributes. You can specify a thread attributes object, or NULL for the default values.

3

start_routine

The C++ routine that the thread will execute once it is created.

4

arg

A single argument that may be passed to start_routine. It must be passed by reference as a pointer cast of type void. NULL may be used if no argument is to be passed

 
The maximum number of threads created by a process depends on the application. Once threads are created, they are of the same type and can spawn other threads. No hierarchy or dependency between threads is assumed.

 

Terminating threads

 The following code is used to terminate POSIX thread

#include <pthread.h>
pthread_exit (status) 

 

Here pthread_exit is explicitly used to exit the thread. Normally, the pthread_exit() routine is called after the thread has finished its work and is no longer needed.

If main() terminates the thread created before it and pthread_exit() exits, other threads continue to execute. Otherwise, they are automatically terminated when main() terminates.

 

Let’s go through some examples for better understanding and implementation: -

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

}

 

Output

main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4

 

Passing an argument to the thread

 This example shows how to pass multiple arguments through a structure. You can pass any data type in a thread callback as it points to null as explained in the following 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 = "This is 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);

}

 Output

 main() : creating thread, 0

main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message

Joining and detaching thread

This code is used to join or detach the thread:

 pthread_join (threadid, status) 
pthread_detach (threadid)


In the above code, the subroutine pthread_join() method blocks the calling thread until the specified 'thread_id's thread ends. When we create a thread, then one of the thread's properties determines whether it can be pinned or unpinned. Only threads created for merging can be merged. If a thread is created as detached, it can never be merged.

This example shows how to wait for a thread to finish using the Pthread assembly routine.

 

#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], &attr, 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);

}

 

Output

 

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 : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... 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

No comments:

Post a Comment

Featured Post

ASSOCIATION RULE IN MACHINE LEARNING/PYTHON/ARTIFICIAL INTELLIGENCE

Association rule   Rule Evaluation Metrics Applications of Association Rule Learning Advantages of Association Rule Mining Disadvantages of ...

Popular