DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Why We Still Struggle With Manual Test Execution in 2025
  • GenAI: From Prompt to Production
  • SAP HANA Triggers: Enhancing Database Logic and Automation
  • GenAI: Running Prototypes Faster Than Wireframes

Trending

  • GitHub Copilot's New AI Coding Agent Saves Developers Time – And Requires Their Oversight
  • Scaling Microservices With Docker and Kubernetes on Production
  • Rust, WASM, and Edge: Next-Level Performance
  • Chat With Your Knowledge Base: A Hands-On Java and LangChain4j Guide

How Can We Control/Schedule Execution of Threads in C, C++?

In this article, we dig into a rather low-level topic, how to schedule and control the execution of threads in a program. To do so, we'll use both C and C++.

By 
Abhijit Pritam Dutta user avatar
Abhijit Pritam Dutta
·
Updated Apr. 10, 18 · Analysis
Likes (4)
Comment
Save
Tweet
Share
52.4K Views

Join the DZone community and get the full member experience.

Join For Free

Download source code from: https://github.com/prateekparallel/InterThreadCommunication

The first two examples are in C and the 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 threads could not access the code) starts executing (code not added, just comments) and finally after completing its task waiting on cond1, likewise, the second thread locked mutex lock2, started executing its business logic, and, finally, waited on conditions from cond2 and the third thread locked mutex lock3, started executing its business logic and finally waited for the condition of cond3.

I am not adding any business logic here because this is just an example. In the sections that are commented out, you can add your business logic which will execute in parallel mode. Suppose thread3 depends on the final output of thread1, which is going to be inserted in a table, and thread3 will read that information before creating its final result, and thread2 depends on the final outcome of thread3 to generate its final outcome. Hence, thread1, after inserting the data into a table, signals thread3 through the condition variable to go ahead with its final process. That means that thread1 controls thread3. As thread2 depends on the final outcome from thread3, thread3 controls the execution of thread2. Here, we can allow thread1 to execute independently as its operation does not depend on any other thread, but, for example, for thread control, we are controlling all the threads here. 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 the operating system once the control passes to the main method/function via the kernel) we are calling pthread_cond_signal(&cond1);. Once this function is called from the main thread, thread1, which was waiting on cond1, will be released and it will start executing further. Once it finishes its final task, it will call pthread_cond_signal(&cond3);. Now, the thread which was waiting on the condition of cond3, i.e. thread3, will be released and it will start to execute its final stage and will call  pthread_cond_signal(&cond2); and it will release the thread which is waiting on the condition of cond2, in this case, thread2. This is the way we can schedule and control the execution of threads in a 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 a global variable as a controller to control threads. Please examine the below example carefully to see how it has been scheduled/controlled based on a global variable. But the 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 the while loop.

#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 the first C example. If you came directly 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 code into separate headers and CPP files. Also, I've declared and defined all the methods inline, as this is only an example. Also, you might think, 'why am I declaring three classes while the class structure is the same?' Don’t be confused, I am using the same class definitions for example purposes only. Look at the commented lines for the business logic. Here, every class will have different business functions and logic. This example is for three different threads with three different classes; just assume that all the classes are different with different functionalities 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;
}
Execution (computing) Business logic

Opinions expressed by DZone contributors are their own.

Related

  • Why We Still Struggle With Manual Test Execution in 2025
  • GenAI: From Prompt to Production
  • SAP HANA Triggers: Enhancing Database Logic and Automation
  • GenAI: Running Prototypes Faster Than Wireframes

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!