Let’s talk about Threads
What is a Thread?
– A Thread is a path of execution within a process. A thread is also known as a lightweight process. The idea is to achieve parallelism by dividing a process into multiple threads.
-A thread is an active entity as it executes as a part of a process. It goes through various stages in its life cycle. A thread is born/created, started, runs and dies.
-A thread has a parentβs process control block (PCB), its own thread control block and stack and common address space.
-Threads within the process run in shared memory space. Thread switching does not need to interact with the operating system, hence threads have lesser context switching time.
-Thread is a lightweight process hence, takes fewer resources when compared with a process.
Threads can be implemented in 2 ways, User level threads and Kernel-level threads. The first one is managed by the user while the second, is managed by the operating system that would acts on the kernel i.e an operating system core.
Few things before we head onto for a simple thread program:
Create a thread
To create a thread, we use pthread_create() function that takes 4 parameters.
1) ID: This is of type pthread_t * . It is simply a pointer to the ID of the thread.
2) Attributes: pthread_attr_t * . This is used to set a few attributes of thread. Such as:
a. Detach State – Affects how you reclaim or leave active resources associated with a thread when a thread ends
( Default: PTHREAD_CREATE_JOINABLE)
b. Scope – Chooses the scheduling contention scope for the created thread.
( Default: PTHREAD_SCOPE_SYSTEM)
c. Inherit scheduler β Affects how the priority of the thread is determined by the system.
( Default: PTHREAD_EXPLICIT_SCHED)
d. Scheduling policy β Affects how the threads are scheduled within the system or within the application. This is related to thread priority.
( Default: SCHED_OTHER )
e. Scheduling priority β Also called as scheduling parameter for the thread.
( Default: 0)
f. Guard Size β Changes the minimum size in bytes of the guard area of the threadβs stack. If this value is set, it will be rounded up to the nearest page size.
( Default: a single page)
g. Stack address – Provides an address for the application managed stack.
(Default: PTHREAD_STACK_MIN)
h. Stack Size β Affects the number of functions that a thread can call before the thread fails due to insufficient stack space
(Default: PTHREAD_STACK_MIN)
i. Stack β Provide both the address and size of an application-managed stack to use for the new thread.
3) Starting routine: It is a void *. This is the name of the function that the thread starts to execute.
4) Arguments: It is a void *. This is the argument that the starting routine takes.
pthread_create(&thread_ID], NULL, Calling_this_function, &the_arg);
To Wait for a thread: Use pthread_join(). The parameters required are:
Thread ID: It is of type pthread_t. This is the ID of the thread that the parent thread waits for.
Reference to return value: It is of type void **. The value of exiting thread is caught by this pointer.
Exiting the thread. Use pthread_exit(). This is written at the end of starting routine.
#include<stdio.h>
#include<pthread.h>
int glb_val = 200;
void *Calling_this_function( void *ptr )
{
printf("Value that was received during the starting routine: %d\n ",*(int*)ptr);
pthread_exit(&glb_val);
}
void main()
{
int the_arg = 10;
int *catch_thread=NULL; // pointer to an integer
pthread_t thread_ID;
pthread_create(&thread_ID,NULL,Calling_this_function,&the_arg); // Create the thread
pthread_join(thread_ID, (void**)&catch_thread);
// Wait for other function and get value in the pointer.
printf("Value received by parent: %d\n",*catch_thread);
}
Execution:
1) Execute with -lpthread
2) The program starts execution from main.
3) Creates a local variable for parent i.e the_arg.
4) Creates a pointer to catch value when the child thread returns i.e catch_thread
5) Creates variable for thread ID i.e thread_ID.
6) Creates a child thread at Calling_this_function() and pass the_arg as parameter.
7) Compiler identifies the Child thread i.e Calling_this_funciton()
8) Enters into the child thread i.e Calling_this_function()
9) Prints the statement with value as 10
10) Compiler waits for the child to die. I mean child process to end so that it can return and catch its value in catch_thread.
11) Execution of child process completed, return the reference to global variable glb_val.
12) Prints the global variable.
13) End of the program.
Another simple program would be:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/types.h>
#define Table_two 20
#define Table_three 21
#define Table_four 22
#define Table_five 23
#define UPTO 5
void *TABLE_Two(void *ptr)
{
printf("...Process ID of Table 2: %d...\n",getpid());
while(1){
int count;
printf("TWO TABLE: \n");
for(count=1;count<=UPTO;count++)
{
printf(" %d * %d = %d\n ",Table_two,count,count*Table_two);
// sleep(1);
}
sleep(2);
}
pthread_exit(NULL);
}
void *TABLE_Three(void *ptr)
{
printf("...Process ID of Table 3: %d...\n",getpid());
while(1){
int count;
printf("\t\tTHREE TABLE: \n");
for(count=1;count<=UPTO;count++)
{
printf("\t\t %d * %d = %d\n ",Table_three,count,count*Table_three);
// sleep(1);
}
sleep(2);
}
pthread_exit(NULL);
}
void *TABLE_Four(void *ptr)
{
printf("...Process ID of Table 4: %d...\n",getpid());
while(1){
int count;
printf("\t\t\t\tFOUR TABLE: \n");
for(count=1;count<=UPTO;count++)
{
printf("\t\t\t\t %d * %d = %d\n ",Table_four,count,count*Table_four);
// sleep(1);
}
sleep(2);
}
pthread_exit(NULL);
}
void *TABLE_Five(void *ptr)
{
printf("...Process ID of Table 5: %d...\n",getpid());
while(1){
int count;
printf("\t\t\t\t\t\tFIVE TABLE: \n");
for(count=1;count<=UPTO;count++)
{
printf("\t\t\t\t\t\t %d * %d = %d\n ",Table_five,count,count*Table_five);
// sleep(1);
}
sleep(2);
}
pthread_exit(NULL);
}
void main()
{
printf("...Process ID of main: %d...\n",getpid());
pthread_t thid[4];
pthread_create(&thid[0],NULL,TABLE_Two,NULL);
pthread_create(&thid[1],NULL,TABLE_Three,NULL);
pthread_create(&thid[2],NULL,TABLE_Four,NULL);
pthread_create(&thid[3],NULL,TABLE_Five,NULL);
pthread_join(thid[0],NULL);
pthread_join(thid[1],NULL);
pthread_join(thid[2],NULL);
pthread_join(thid[3],NULL);
printf("\n....End of threads execution...\n");
}
On execution, you would find that all four threads would be running at the same time.
Feel free to modify and experiment.