-
Notifications
You must be signed in to change notification settings - Fork 2
Home
How can we control/schedule execution of threads in C, C++?
I am actively involved with stackoverflow from 2nd October 2017 to help programmer/student with my limited knowledge. Couple of times I noticed that people ask the above question for C and C++. Decided to write some example codes in C and C++ with different approach which I am explaining below. Suggestions and criticism are most welcome to improve the answer for every one who can be benefited from it.
First two examples are in C and last one is in C++. In my first approach I am using 3 mutexs and 3 condition variables. With the below examples you can schedule or control any number of threads in C and C++. First look at the first thread below. Here it locked mutex lock1 (so that other thread could not access the codes) starts executing (codes not added just comments) and finally after completing its task waiting on cond1, likewise second thread locked mutex lock2, starts executing its business logic and finally waits on condition cond2 and 3rd thread locked mutex lock3, starts executing its business logic and finally waits on condition cond3. I am not adding any business logic here because this is just an example. In the commented section you can add your business logic which will execute in parallel mode. Suppose thread3 depends on final output of thread1 which is going to be inserted in a table and thread3 will read that information before creating it final result and thread2 depends on final outcome of thread3 to generate its final outcome. Hence thread1 after inserting the data into table, signals thread3 through condition variable to go ahead with its final process. That means thread1 controls thread3. As thread2 depends on final outcome from thread3, hence thread3 controls the execution of Thread2. Here we can allow thread1 to execute independently as its operation does not depends on any other thread, but for example of thread control we are controlling all the threads here and hence thread1 is being controlled from thread2.
To start the controlling process, we are releasing thread1 first. In the main thread (i.e. main function, every program has one main thread, in C/C++ this main thread is created automatically by operating system once the control pass to the main method/function by kernel) we are calling pthread_cond_signal(&cond1); Once this function called from main thread, thread1 which was waiting on cond1 will be released and it will start executing further. Once it finishes with its final task, it will call pthread_cond_signal(&cond3); now thread which was waiting on condition cond3 i.e. thread3 will be released and it will start to execute it’s final stage and will call pthread_cond_signal(&cond2); and it will release the thread which is waiting on condition cond2 i.e. in this case thread2. This is the way we can schedule and control execution of thread in multi-threaded environment.
#include<pthread.h>
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond3 = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock3 = PTHREAD_MUTEX_INITIALIZER;
int TRUE = 1;
void print(char *p)
{
printf("%s",p);
}
void * threadMethod1(void *arg)
{
printf("In thread1\n");
do{
pthread_mutex_lock(&lock1);
//Add your business logic(parallel execution codes) here
pthread_cond_wait(&cond1, &lock1);
printf("I am thread1 generating the final report and inserting into a table \n");
pthread_cond_signal(&cond3);/* Now allow 3rd thread to process */
pthread_mutex_unlock(&lock1);
}while(TRUE);
pthread_exit(NULL);
}
void * threadMethod2(void *arg)
{
printf("In thread2\n");
do
{
pthread_mutex_lock(&lock2);
//Add your business logic(parallel execution codes) here
pthread_cond_wait(&cond2, &lock2);
printf("I am thread2 generating the final report and inserting into a table \n");
pthread_cond_signal(&cond1);
pthread_mutex_unlock(&lock2);
}while(TRUE);
pthread_exit(NULL);
}
void * threadMethod3(void *arg)
{
printf("In thread3\n");
do
{
pthread_mutex_lock(&lock3);
//Add your business logic(parallel execution codes) here
pthread_cond_wait(&cond3, &lock3);
printf("I am thread3 generating the final report and inserting into a table \n");
pthread_cond_signal(&cond2);
pthread_mutex_unlock(&lock3);
}while(TRUE);
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid1, tid2, tid3;
int i = 0;
printf("Before creating the threads\n");
if( pthread_create(&tid1, NULL, threadMethod1, NULL) != 0 )
printf("Failed to create thread1\n");
if( pthread_create(&tid2, NULL, threadMethod2, NULL) != 0 )
printf("Failed to create thread2\n");
if( pthread_create(&tid3, NULL, threadMethod3, NULL) != 0 )
printf("Failed to create thread3\n");
pthread_cond_signal(&cond1);/* Now allow first thread to process first */
sleep(1);
TRUE = 0;/* Stop all the thread */
sleep(3);
/* this is how we join thread before exit from a system */
/*
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);*/
exit(0);
}
In my second approach I am using global variable as controller to control threads. Please see the below example carefully how it has been scheduled/controlled based on a global variable. But best approach is the first example, the below is just for understanding. Here I don’t need to explain the logic. Just check the “if condition“ inside while loop, you will understand it very easily.
#include<pthread.h>
int controller = 0;
void print(char *p)
{
printf("%s",p);
}
void * threadMethod1(void *arg)
{
while(1)
{
if(controller == 3)
break;
}
print("I am thread 1st\n");
controller = 1;
pthread_exit(NULL);
}
void * threadMethod2(void *arg)
{
while(1)
{
if(controller == 1)
break;
}
print("I am thread 2nd\n");
controller = 2;
pthread_exit(NULL);
}
void * threadMethod3(void *arg)
{
while(1)
{
if(controller == 0)
break;
}
print("I am thread 3rd\n");
controller = 3;
pthread_exit(NULL);
}
int main(void)
{
pthread_t tid1, tid2, tid3;
int i = 0;
printf("Before creating the threads\n");
if( pthread_create(&tid1, NULL, threadMethod1, NULL) != 0 )
printf("Failed to create thread1\n");
if( pthread_create(&tid2, NULL, threadMethod2, NULL) != 0 )
printf("Failed to create thread2\n");
sleep(3);
if( pthread_create(&tid3, NULL, threadMethod3, NULL) != 0 )
printf("Failed to create thread3\n");
/*
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);*/
sleep(10);
exit(0);
}
Now my third example is in C++. Here I am using the same approach as I have applied in first C example. If you directly come to this example please read my very first example in C to understand the approach. In the below example I have developed the code in Visual Studio 2013 and I am not distributing the codes into separate header and cpp files. Also declared and defined all the method inline as this is simply an example only. Also you might thing that why I am declaring 3 classes while class structure is same. Don’t be confused, for example only I am using same class definition. Look at the commented line for business logic. Here every class will have different business function and logic. This example is for 3 different threads with 3 different classes; just assume that all the classes are different with different functionality and business logic.
#include "stdafx.h"//remove this header file if you are compiling with different compiler
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include "windows.h"//remove this header file if you are compiling with different compiler
std::condition_variable _tcond1;
std::condition_variable _tcond2;
std::condition_variable _tcond3;
class SimpleThread1
{
private:
std::mutex _lockprint;
bool isThreadAlive = true;
public:
SimpleThread1(){}
SimpleThread1(SimpleThread1 &st){};
void StartProcessing()
{
std::unique_lock<std::mutex> locker(_lockprint);
//Add your business logic(parallel execution codes) here
_tcond1.wait(locker);
std::cout << "I am thread :1"<<std::endl;
_tcond3.notify_one();
}
void operator()()
{
while (isThreadAlive)
StartProcessing();
}
void stopeThread()
{
isThreadAlive = false;
}
};
class SimpleThread2
{
private:
std::mutex _lockprint;
bool isThreadAlive = true;
public:
SimpleThread2(){}
SimpleThread2(SimpleThread2 &st) {};
void StartProcessing()
{
std::unique_lock<std::mutex> locker(_lockprint);
//Add your business logic(parallel execution codes) here
_tcond2.wait(locker);
std::cout << "I am thread :2"<< std::endl;
_tcond1.notify_one();
}
void operator()()
{
while (isThreadAlive)
StartProcessing();
}
void stopeThread()
{
isThreadAlive = false;
}
};
class SimpleThread3
{
private:
std::mutex _lockprint;
bool isThreadAlive = true;
public:
SimpleThread3(){}
SimpleThread3(SimpleThread3 &st) {};
void StartProcessing()
{
std::unique_lock<std::mutex> locker(_lockprint);
//Add your business logic(parallel execution codes) here
_tcond3.wait(locker);
std::cout << "I am thread :3"<< std::endl;
_tcond2.notify_one();
}
void operator()()
{
while (isThreadAlive)
StartProcessing();
}
void stopeThread()
{
isThreadAlive = false;
}
};
int main()
{
SimpleThread1 st1;
SimpleThread2 st2;
SimpleThread3 st3;
std::thread t1(st1);
std::thread t2(st2);
std::thread t3(st3);
_tcond1.notify_one();
t1.detach();
t2.detach();
t3.detach();
Sleep(1000);//replace it with sleep(10) for linux/unix
st1.stopeThread();
st2.stopeThread();
st3.stopeThread();
return 0;
}