Multithreading
- WHAT IS MULTITHREADING
- ITS PARAMETER AND DESCRIPTION
- TERMINATION THREADS
- PASSING ARGUMENT TO THREAD
- JOINING AND DETACHING THREAD
- WHAT IS MEMORY MANAGEMENT
- WHY IS IT REQUIRED
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.
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