Cpp program using producer and consumer (mutex locks) on a shared buffer - c++

I have a c++ code where I am trying to produce some values into thread and consume it using 2 other consumer threads. Mutex locks are used to provide synhronisation. But output does not show a random progress for the threads, as in, producer produces 5 at a time, 1 of the consumer consumes it all at a time not giving the other consumer or the producer a chance before it completely consumes the queue. ANy help would be much appreciated. I am also attaching the code and sample output.
// CPP program to demonstrate the given task
`#include <iostream>`
`#include <pthread.h>`
#include <queue>
#include <stdlib.h>
#include<unistd.h>
#define MAX 10
using namespace std;
// Declaring global variables
int sum_B = 0, sum_C = 0;
int consumerCount1 = 0;
int consumerCount2 = 0;
// Shared queue
queue<int> Q;
int item=0;
// Function declaration of all required functions
void* producerFun(void*);
void* add_B(void*);
void* add_C(void*);
// Getting the mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t dataNotProduced =
PTHREAD_COND_INITIALIZER;
pthread_cond_t dataNotConsumed =
PTHREAD_COND_INITIALIZER;
// Function to generate random numbers and
// push them into queue using thread A
void* producerFun(void*)
{
static int producerCount = 0;
// Initialising the seed
srand(time(NULL));
while (1) {
// Getting the lock on queue using mutex
pthread_mutex_lock(&mutex);
if (Q.size() < MAX && item < MAX)
{
// Getting the random number
int num = rand() % 10 + 1;
// Pushing the number into queue
Q.push(num);
producerCount++;
item++;
cout << "Produced: " << num << " item: "<<item<<endl;
pthread_cond_broadcast(&dataNotProduced);
}
// If queue is full, release the lock and return
else if (item == MAX) {
pthread_mutex_unlock(&mutex);
cout<<"\nLeaving";
//sleep(1);
continue;
}
// If some other thread is exectuing, wait
/*else {
cout << ">> Producer is in wait.." << endl;
pthread_cond_wait(&dataNotConsumed, &mutex);
}*/
// Get the mutex unlocked
pthread_mutex_unlock(&mutex);
//sleep(0.5);
}
}
// Function definition for consumer thread B
void* add_B(void*)
{
while (1) {
// Getting the lock on queue using mutex
pthread_mutex_lock(&mutex);
// Pop only when queue has at least 1 element
if (Q.size() > 0) {
// Get the data from the front of queue
int data = Q.front();
// Add the data to the integer variable
// associated with thread B
sum_B += data;
consumerCount1++;
// Pop the consumed data from queue
Q.pop();
item--;
cout << "B thread consumed: " << data << "cc1: "<<consumerCount1<<endl;
//pthread_cond_signal(&dataNotConsumed);
}
// Check if consmed numbers from both threads
// has reached to MAX value
/*else if (consumerCount2 + consumerCount1 == MAX) {
pthread_mutex_unlock(&mutex);
return NULL;
}*/
// If some other thread is exectuing, wait
else {
cout << "B is in wait.." << endl;
pthread_cond_wait(&dataNotProduced, &mutex);
}
// Get the mutex unlocked
pthread_mutex_unlock(&mutex);
//sleep(0.5);
}
}
// Function definition for consumer thread C
void* add_C(void*)
{
while (1) {
// Getting the lock on queue using mutex
pthread_mutex_lock(&mutex);
// Pop only when queue has at least 1 element
if (Q.size() > 0) {
// Get the data from the front of queue
int data = Q.front();
// Add the data to the integer variable
// associated with thread B
sum_C += data;
// Pop the consumed data from queue
Q.pop();
item--;
consumerCount2++;
cout << "C thread consumed: " << data << "cc2: "<<consumerCount2<<endl;
//pthread_cond_signal(&dataNotConsumed);
}
// Check if consmed numbers from both threads
// has reached to MAX value
/*else if (consumerCount2 + consumerCount1 == MAX)
{
pthread_mutex_unlock(&mutex);
return NULL;
}*/
// If some other thread is exectuing, wait
else {
cout << ">> C is in wait.." << endl;
// Wait on a condition
pthread_cond_wait(&dataNotProduced, &mutex);
}
// Get the mutex unlocked
pthread_mutex_unlock(&mutex);
//sleep(0.5);
}
}
// Driver code
int main()
{
// Declaring integers used to
// identify the thread in the system
pthread_t producerThread, consumerThread1, consumerThread2;
// Function to create a threads
// (pthread_create() takes 4 arguments)
int retProducer = pthread_create(&producerThread,
NULL, producerFun, NULL);
int retConsumer1 = pthread_create(&consumerThread1,
NULL, *add_B, NULL);
int retConsumer2 = pthread_create(&consumerThread2,
NULL, *add_C, NULL);
// pthread_join suspends execution of the calling
// thread until the target thread terminates
//if (!retProducer)
pthread_join(producerThread, NULL);
//if (!retConsumer1)
pthread_join(consumerThread1, NULL);
//if (!retConsumer2)
pthread_join(consumerThread2, NULL);
// Checking for the final value of thread
if (sum_C > sum_B)
cout << "Winner is Thread C" << endl;
else if (sum_C < sum_B)
cout << "Winner is Thread B" << endl;
else
cout << "Both has same score" << endl;
return 0;
}
enter image description here

Related

Why a waiting thread does not wake up after calling notify?

I am trying to simulate a sensor that outputs data at a certain frame rate while another is waiting to have a data ready and when it is ready it copies it locally and processes it.
Sensor sensor(1,1000);
Monitor monitor;
// Function that continuously reads data from sensor
void runSensor()
{
// Initial delay
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
for(int i = 0; i < SIZE_LOOP; i++)
{
monitor.captureData<Sensor>(sensor, &Sensor::captureData);
}
}
// Function that waits until sensor data is ready
void waitSensor()
{
monitor.saveData<Sensor>(sensor, &Sensor::saveData);
}
// Main function
int main()
{
// Threads that reads at some frame rate data from sensor
std::thread threadRunSensor(runSensor);
// Processing loop
for(int i = 0; i < SIZE_LOOP; i++)
{
// Wait until data from sensor is ready
std::thread threadWaitSensor(waitSensor);
// Wait until data is copied
threadWaitSensor.join();
// Process synchronized data while sensor are throwing new data
std::cout << "Init processing (" << sensor.getData() << /*"," << sensor2.getData() << */")"<< std::endl;
// Sleep to simulate processing load
std::this_thread::sleep_for(std::chrono::milliseconds(10000 + (rand() % 1000)));
//std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "End processing" << std::endl;
}
return 0;
}
This is the sensor class. It has two methods. One that generates the data and other that copies the data locally.
class Sensor
{
private:
int counter;
int id;
int frameRate;
int dataCaptured;
int dataSaved;
public:
Sensor(int f_id, int f_frameRate)
{
id = f_id;
counter = 0;
frameRate = f_frameRate;
};
~Sensor(){};
void captureData()
{
dataCaptured = counter;
counter ++;
std::cout << "Sensor" << id << " (" << dataCaptured << ")"<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(frameRate + (rand() % 500)));
};
void saveData()
{
dataSaved = dataCaptured;
std::cout << "Copying sensor" << id << " (" << dataSaved << ")"<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1 + (rand() % 5)));
}
int getData()
{
return dataSaved;
}
};
Then there is a class Monitor that ensures these operations are protected to concurrent accesses.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <cstdlib>
#define SIZE_LOOP 1000
class Monitor
{
private:
std::mutex m_mutex;
std::condition_variable m_condVar;
bool m_isReady;
public:
Monitor()
{
init();
};
~Monitor()
{
};
void init()
{
m_isReady = false;
};
template<class T>
void captureData(T& objectCaptured, void (T::* f_captureFunction_p)())
{
// Lock read
std::unique_lock<std::mutex> lock = std::unique_lock<std::mutex>(m_mutex);
(objectCaptured.*f_captureFunction_p)();
m_isReady = true;
m_condVar.notify_one();
lock.unlock();
};
template<class T>
void saveData(T& objectSaved, void(T::*f_saveFunction_p)())
{
std::unique_lock<std::mutex> lock = std::unique_lock<std::mutex>(m_mutex);
while(!m_isReady)
{
m_condVar.wait(lock);
}
(objectSaved.*f_saveFunction_p)();
m_isReady = false;
lock.unlock();
};
};
Can anyone tell me why the waiting thread does not wakeup if the sensor is notifyng every frame rate?
The idea is having two threads with this workflow:
ThreadCapture captures a data consinuously notifying to ThreadProcessing when the data capture is done.
ThreadCapture must waits to capture a new data only if the current captured data is being copied on ThreadProcessing.
ThreadProcessing waits to a new captured data, makes a local copy, notifies to ThreadCapture that the copy is done and process the data.
The local copy is made on ThreadProcessing to allow ThreadCapture can capture new data while ThreadProcessing is processing.
Finally I found the solution adding a waiting step after the capture to give time to save data
template<class T>
void captureData(T& objectCaptured, void (T::* f_captureFunction_p)())
{
std::unique_lock<std::mutex> lockReady = std::unique_lock<std::mutex>(m_mutexReady, std::defer_lock);
std::unique_lock<std::mutex> lockProcess = std::unique_lock<std::mutex>(m_mutexProcess, std::defer_lock);
// Lock, capture, set data ready flag, unlock and notify
lockReady.lock();
(objectCaptured.*f_captureFunction_p)();
m_isReady = true;
lockReady.unlock();
m_conditionVariable.notify_one();
// Wait while data is ready and it is not being processed
lockReady.lock();
lockProcess.lock();
while(m_isReady && !m_isProcessing)
{
lockProcess.unlock();
m_conditionVariable.wait(lockReady);
lockProcess.lock();
}
lockProcess.unlock();
lockReady.unlock();
};
template<class T>
void saveData(T& objectSaved, void(T::*f_saveFunction_p)())
{
std::unique_lock<std::mutex> lockReady(m_mutexReady, std::defer_lock);
std::unique_lock<std::mutex> lockProcess(m_mutexProcess, std::defer_lock);
// Reset processing
lockProcess.lock();
m_isProcessing = false;
lockProcess.unlock();
// Wait until data is ready
lockReady.lock();
while(!m_isReady)
{
m_conditionVariable.wait(lockReady);
}
// Make a copy of the data, reset ready flag, unlock and notify
(objectSaved.*f_saveFunction_p)();
m_isReady = false;
lockReady.unlock();
m_conditionVariable.notify_one();
// Set processing
lockProcess.lock();
m_isProcessing = true;
lockProcess.unlock();
};
};

Where to place pthread mutex and con var to ensure all functions are activated?

I have the following program that should take in "orders" and process them synchronously. I think most of it works, however critical region 4 doesn't get the mutex until after all of the orders are placed. Where should the cond_vars be placed so that region 4 gets the lock while orders are still being produced?
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <cstdlib>
using namespace std;
#define MAX 10
#define N 4
// Data structure to represent a simplified Order
// that has an order number and an item number.
struct Order
{
int order_num;
int item_num;
};
Order new_orders [N]; // array of elements of type Order to be used as
a shared buffer
int num_new_orders = 0; // count of number of new (i.e., unprocessed)
orders
int order_num = 0; // global variable used to generate unique order
numbers
pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t console_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t spaceAvailabe = PTHREAD_COND_INITIALIZER;
pthread_cond_t newOrder = PTHREAD_COND_INITIALIZER;
pthread_cond_t consoleVar = PTHREAD_COND_INITIALIZER;
void* takeOrders(void* arg)
{
int item;
int index = 0;
for(int i = 0; i < MAX; ++i) {
// Beginning of critical region 1
pthread_mutex_lock(&console_mutex);
// Get user input
cout << "Enter a menu item number between 1 and 50: ";
cin >> item;
// Print new order's details
cout << "Got new order! Order number is " << order_num <<
" and item number: " << item << std::endl;
// End of critical region 1
pthread_mutex_unlock(&console_mutex);
// Beginning of critical region 2
pthread_mutex_lock(&data_mutex);
// Put new order into new orders buffer and update number of new orders
while(num_new_orders>=MAX){
pthread_cond_wait(&spaceAvailabe, &console_mutex);
}
new_orders[index].order_num = order_num;
new_orders[index++].item_num = item;
++num_new_orders;
pthread_cond_signal(&newOrder);
// End of critical region 2
pthread_mutex_unlock(&data_mutex);
// Update order number so that next order gets a different number
++order_num;
// If the end of the new orders buffer is reached, wrap back around
if(index == N)
index = 0;
}
pthread_exit(NULL);
}
void* processOrders(void* arg)
{
int item;
int index = 0;
int o_num;
for(int i = 0; i < MAX; ++i) {
// Beginning of critical region 3
pthread_mutex_lock(&data_mutex);
// Retrieve new order details from buffer and update number of new orders
while(num_new_orders==0)
{
pthread_cond_wait(&newOrder, &data_mutex);
}
o_num = new_orders[index].order_num;
item = new_orders[index++].item_num;
--num_new_orders;
pthread_cond_signal(&spaceAvailabe);
// End of critical region 3
pthread_mutex_unlock(&data_mutex);
// Beginning of critical region 4
pthread_mutex_lock(&console_mutex);
// Print retrieved order's details
cout << "Processing order number " << o_num << " with item number: " <<
item << std::endl;
// End of critical region 4
pthread_mutex_unlock(&console_mutex);
// Suspend self for 1 second
sleep(1);
// If the end of the new orders buffer is reached, wrap back around
if(index == N)
index = 0;
}
pthread_exit(NULL);
}
int main()
{
// Create threads to take and process orders
pthread_t id1, id2;
pthread_create(&id1, NULL, processOrders, NULL);
pthread_create(&id2, NULL, takeOrders, NULL);
pthread_join(id1, NULL);
pthread_join(id2, NULL);
// Print goodbye message
cout << "Phew! Done with orders for today!" << endl;
pthread_exit(NULL);
}
I have written a program couple of months back How to execute thread in sync order. I am posting the same program here. Which might help you. Also do not mix C++ code with C. Better use stdio.h instead of iostream. In C++ 11 implementation of thread is much easier than C.
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);
pthread_cond_wait(&cond1, &lock1);
print("I am thread 1st\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);
pthread_cond_wait(&cond2, &lock2);
print("I am thread 2nd\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);
pthread_cond_wait(&cond3, &lock3);
print("I am thread 3rd\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 */
/*
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);*/
sleep(1);
TRUE = 0;/* Stop all the thread */
sleep(3);
exit(0);
}

pthread_cond_wait wake many threads example

pthread_cond_wait wake many threads example
Code to wake up thread 1 & 3 on some broadcast from thread 0.
Setup: Win7 with mingw32, g++ 4.8.1 with mingw32-pthreads-w32
pthread condition variable
Solution:
http://pastebin.com/X8aQ5Fz8
#include <iostream>
#include <string>
#include <list>
#include <map>
#include <pthread.h>
#include <fstream>
#include <sstream> // for ostringstream
#define N_THREAD 7
using namespace std;
// Prototypes
int main();
int scheduler();
void *worker_thread(void *ptr);
string atomic_output(int my_int, int thread_id);
// Global variables
//pthread_t thread0, thread1, thread2, thread3, thread4, thread5, thread6, thread7;
pthread_t m_thread[N_THREAD];
int count = 1;
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;
// Main
int main() {
cout << "Launching main. \n";
//Start to monitor for exceptions
register_exception_handler();
//Start scheduler
scheduler();
return 0;
}
// Scheduler
int scheduler() {
// Starting scheduler log file
ofstream scheduler_log;
scheduler_log.open ("scheduler_log.txt");
//scheduler_log << "[Scheduler] Starting." << endl;
cout << "[Scheduler] Starting. \n";
// Scheduler::Main Section
int thread_id[N_THREAD];
for(int i=0;i<N_THREAD;i++) {
thread_id[i] = i;
pthread_create( &m_thread[i], NULL, worker_thread, (void *) &thread_id[i]);
}
for(int i=0;i<N_THREAD;i++)
pthread_join(m_thread[i], NULL);
cout << "[Scheduler] Ending. \n";
// Closing scheduler log file
scheduler_log.close();
return 0;
}
string atomic_output(int my_int, int thread_id) {
ostringstream stm;
stm << "Thread ";
stm << thread_id;
stm << ": ";
//count fn
stm << my_int;
stm << "\n";
//stm << "Finished. \n";
return stm.str();
}
void *worker_thread(void *ptr) {
string line;
//int boo = 0;
int thread_id = *(int *) ptr;
//if(thread_id == 0)
// pthread_mutex_lock( &count_mutex );
for(int i=0;i<10;i++) {
//boo++;
if (thread_id == 1) {
pthread_mutex_lock(&count_mutex);
while (count == 1) {
cout << "[Thread 1] Before pthread_cond_wait...\n";
pthread_cond_wait( &condition_var, &count_mutex );
cout << "[Thread 1] After pthread_cond_wait...\n";
}
pthread_mutex_unlock(&count_mutex);
}
if (thread_id == 3) {
pthread_mutex_lock(&count_mutex);
while (count == 1) {
cout << "[Thread 3] Before pthread_cond_wait...\n";
pthread_cond_wait( &condition_var, &count_mutex );
cout << "[Thread 3] After pthread_cond_wait...\n";
}
pthread_mutex_unlock(&count_mutex);
}
//count fn
line = atomic_output(i, *(int *)ptr);
cout << line;
if (i == 5) {
if(thread_id == 0) {
pthread_mutex_lock( &count_mutex );
count = 0;
pthread_mutex_unlock( &count_mutex );
pthread_cond_broadcast(&condition_var);
}
}
}
//line = atomic_output(0, *(int *)ptr);
//cout << line;
}
(old) -= What I've tried =-
*Edit: early problem in the code with while(0) instead of while(predicate). Keeping it there for easy reference with the comments.
Code 1: http://pastebin.com/rCbYjPKi
I tried to while(0) pthread_cond_wait( &condition_var, &count_mutex );
with pthread_cond_broadcast(&condition_var); ... The thread does not respect the condition.
Proof of condition non-respect : http://pastebin.com/GW1cg4fY
Thread 0: 0
Thread 0: 1
Thread 0: 2
Thread 0: 3
Thread 2: 0
Thread 6: 0
Thread 1: 0 <-- Here, Thread 1 is not supposed to tick before Thread 0 hit 5. Thread 0 is at 3.
Code 2: http://pastebin.com/g3E0Mw9W
I tried pthread_cond_wait( &condition_var, &count_mutex ); in thread 1 and 3 and the program does not return.
either thread 1, or thread 3 waits forever. Even using broadcast which says it should wake up all waiting threads. Obviously something is not working, code or lib?
More:
I've tried to unlock the mutex first, then broadcast. I've tried to broadcast then unlock. Both don't work.
I've tried to use signal instead of broadcast, same problem.
References that I can't make work (top google search)
http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
http://docs.oracle.com/cd/E19455-01/806-5257/6je9h032r/index.html
http://www-01.ibm.com/support/knowledgecenter/ssw_i5_54/apis/users_76.htm
Code 3: http://pastebin.com/tKP7F8a8
Trying to use a predicate variable count, to fix race problem condition. Still a problem, doesn't prevent thread1 and thread3 from running when thread0 is between 0 and 5.
What would be the code to wake up thread 1 & 3 on some function call from thread0
if(thread_id == 0)
pthread_mutex_lock( &count_mutex );
for(int i=0;i<10;i++) {
//boo++;
if (thread_id == 1) {
while(0)
pthread_cond_wait( &condition_var, &count_mutex );
}
None of this makes any sense. The correct way to wait for a condition variable is:
pthread_mutex_lock(&mutex_associated_with_condition_variable);
while (!predicate)
pthread_cond_wait(&condition_variable, mutex_associated_with_condition_variable);
Notice:
The mutex must be locked.
The predicate (thing you are waiting for) must be checked before waiting.
The wait must be in a loop.
Breaking any of these three rules will cause the kind of problems you are seeing. Your main problem is that you break the second rule, waiting even when the thing you want to wait for has already happened.

Producers, Consumers, and my cute Deadlock

I'm working on a Producer Consumer (Multithreaded) problem and I'm encountering a deadlock. My question is how?
I have multiple producers creating n products, and putting them in a global queue. The producers must WAIT if there is no room in the queue.
My consumers are accessing the queue by using First Come First Serve. The consumer must WAIT if there is nothing in the queue. The consumer will consume PART of the product, and the product will only be removed from the queue when it has been totally consumed. The consumers will stop consuming when there are no more products to be consumed.
I get the deadlock when the first item is done being consumed. I am using a mutex to lock the queue, 2 condition variables to signal when an item is added or removed to the queue, and a semaphore to keep track of total items being consumed.
Any idea why it may be deadlocking?
Input: 2 (Producers) 3 (Consumers) 5 (Items to produce) 2 (Queue Size) 0 (N/A) 50 (Quantum) 1 (Seed)
Producer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
#include <sys/time.h>
void* producer(void* args)
{
tProducer producer = *((tProducer*) args);
tProduct products[producer.products];
unsigned int queueMax = producer.queueMax;
timeval time;
std::string output;
for(int i = 0; i < producer.products; i++)
{
//Create item
products[i].productId = i;
products[i].life = producer.lifeOfProduct;
gettimeofday(&time, NULL);
products[i].timestamp = time.tv_sec;
//Lock and add to queue
pthread_mutex_lock(&queueLock);
//Queue is full and must wait
if(queue.size() >= queueMax)
{
output = "Producer: " + std::to_string(producer.id) + " is waiting\n";
std::cout << output;
pthread_cond_wait(&removeSignal, &queueLock);
}
//Debug message
output = "Producer: " + std::to_string(producer.id) + " is producing.\n";
std::cout << output;
//Add item to queue and signal
queue.push(products[i]);
pthread_cond_signal(&addSignal);
pthread_mutex_unlock(&queueLock);
//pthread_cond_signal(&addSignal);
//Debug message
output = "Producer: " + std::to_string(producer.id) + " just produced.\n";
std::cout << output;
}
pthread_exit(NULL);
}
Consumer:
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
#include <iostream>
void* consumer(void* args)
{
tConsumer consumer = *((tConsumer*) args);
int id = consumer.id;
int quantum = consumer.quantum;
std::string output;
while(true)
{
//Exit when nothing else is being created
if(sem_trywait(&totalProductsLeft) < 0)
{
break;
}
//Subtract life from product, and remove from queue if done
pthread_mutex_lock(&queueLock);
//Wait until item is in queue
if(queue.size() <= 0)
{
//Debug message
output = "Consumer: " + std::to_string(id) + " is waiting.\n";
std::cout << output;
pthread_cond_wait(&addSignal, &queueLock);
}
//Debug message
output = "Consumer: " + std::to_string(id) + " is ready.\n";
std::cout << output;
tProduct& product = queue.front();
product.life -= quantum;
//Item is done being consumed
if(product.life <= 0)
{
//Debug message
output = "Product: " + std::to_string(product.productId) + " is dead.\n";
std::cout << output;
//Remove a spot
queue.pop();
pthread_cond_signal(&removeSignal);
sem_wait(&totalProductsLeft);
}
else
{
//Debug message
output = "Product: " + std::to_string(product.life) + "hp is not done.\n";
std::cout << output;
}
pthread_mutex_unlock(&queueLock);
}
//May need to broadcast
pthread_exit(NULL);
}
Main (Just to show how I initialize everything):
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <pthread.h>
#include <semaphore.h>
#include "producerConsumer.h"
std::queue<tProduct> queue;
pthread_cond_t addSignal;
pthread_cond_t removeSignal;
sem_t totalProductsLeft;
pthread_mutex_t queueLock;
int main(int argc, char** argv)
{
//Handle input
const int NUM_INPUTS = 8;
int numberProducers;
int numberConsumers;
int numberOfProducts;
int queueSize;
int scheduleType;
int quantum;
int seed;
//Error check for input
if(argc != NUM_INPUTS)
{
std::cout << "Invalid number of arguments.\n";
return -1;
}
//Grab arguments
numberProducers = atoi(argv[1]);
numberConsumers = atoi(argv[2]);
numberOfProducts = atoi(argv[3]);
queueSize = atoi(argv[4]);
scheduleType = atoi(argv[5]);
quantum = atoi(argv[6]);
seed = atoi(argv[7]);
//Get rid of warnings for now
std::cout << numberOfProducts << std::endl;
std::cout << queueSize << std::endl;
std::cout << quantum << std::endl;
std::cout << seed << std::endl;
std::cout << scheduleType << std::endl;
//Create threads
pthread_t producerThreads[numberProducers];
pthread_t consumerThreads[numberConsumers];
tProducer producerArgs[numberProducers];
tConsumer consumerArgs[numberConsumers];
//Initialize global
pthread_mutex_init(&queueLock, NULL);
pthread_cond_init(&addSignal, NULL);
pthread_cond_init(&removeSignal, NULL);
std::cout << "Total Items: " << (numberProducers * numberOfProducts) << std::endl;
sem_init(&totalProductsLeft, 0, numberProducers * numberOfProducts);
//Start threads
srand(seed);
for(int i = 0; i < numberProducers; i++)
{
producerArgs[i].id = i;
producerArgs[i].products = numberOfProducts;
producerArgs[i].lifeOfProduct = rand() % 1024;
producerArgs[i].queueMax = queueSize;
pthread_create(&(producerThreads[i]), 0, producer, &producerArgs[i]);
}
for(int i = 0; i < numberConsumers; i++)
{
consumerArgs[i].id = i;
consumerArgs[i].quantum = quantum;
pthread_create(&(consumerThreads[i]), 0, consumer, &consumerArgs[i]);
}
//Wait for threads to end
for(int i = 0; i < numberProducers; i++)
{
pthread_join(producerThreads[i], NULL);
}
for(int i = 0; i < numberConsumers; i++)
{
pthread_join(consumerThreads[i], NULL);
}
return 0;
}
I ended up figuring it out. sem_trywait, in my consumer, is decrementing when items weren't done being consumed. The sem_wait inside my consumer is then blocking because there are no items left.

Boost synchronization

I have NUM_THREADS threads, with the following codes in my thread:
/*
Calculate some_value;
*/
//Critical section to accummulate all thresholds
{
boost::mutex::scoped_lock lock(write_mutex);
T += some_value;
num_threads++;
if (num_threads == NUM_THREADS){
T = T/NUM_THREADS;
READY = true;
cond.notify_all();
num_threads = 0;
}
}
//Wait for average threshold to be ready
if (!READY)
{
boost::unique_lock<boost::mutex> lock(wait_mutex);
while (!READY){
cond.wait(lock);
}
}
//End critical section
/*
do_something;
*/
Basically, I want all the threads to wait for the READY signal before continuing. num_thread is set to 0, and READY is false before threads are created. Once in a while, deadlock occurs. Can anyone help please?
All the boost variables are globally declared as follows:
boost::mutex write_mutex;
boost::mutex wait_mutex;
boost::condition cond;
The code has a race condition on the READY flag (which I assume is just a bool variable). What may happen (i.e. one possible variant of thread execution interleaving) is:
Thread T1: Thread T2:
if (!READY)
{
unique_lock<mutex> lock(wait_mutex); mutex::scoped_lock lock(write_mutex);
while (!READY) /* ... */
{ READY = true;
/* !!! */ cond.notify_all();
cond.wait(lock);
}
}
The code testing the READY flag is not synchronized with the code setting it (note the locks are different for these critical sections). And when T1 is in a "hole" between the flag test and waiting at cond, T2 may set the flag and send a signal to cond which T1 may miss.
The simplest solution is to lock the right mutex for the update of READY and condition notification:
/*...*/
T = T/NUM_THREADS;
{
boost::mutex::scoped_lock lock(wait_mutex);
READY = true;
cond.notify_all();
}
It looks like Boost.Thread's barriers might be what you need.
Here's a working example that averages values provided by several worker threads. Each worker thread uses the same shared barrier (via the accumulator instance) to synchronize each other.
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
boost::mutex coutMutex;
typedef boost::lock_guard<boost::mutex> LockType;
class Accumulator
{
public:
Accumulator(int count) : barrier_(count), sum_(0), count_(count) {}
void accumulateAndWait(float value)
{
{
// Increment value
LockType lock(mutex_);
sum_ += value;
}
barrier_.wait(); // Wait for other the threads to wait on barrier.
}
void wait() {barrier_.wait();} // Wait on barrier without changing sum.
float sum() {LockType lock(mutex_); return sum_;} // Return current sum
float average() {LockType lock(mutex_); return sum_ / count_;}
// Reset the sum. The barrier is automatically reset when triggered.
void reset() {LockType lock(mutex_); sum_ = 0;}
private:
typedef boost::lock_guard<boost::mutex> LockType;
boost::barrier barrier_;
boost::mutex mutex_;
float sum_;
int count_;
};
/* Posts a value for the accumulator to add and waits for other threads
to do the same. */
void workerFunction(Accumulator& accumulator)
{
// Sleep for a random amount of time before posting value
int randomMilliseconds = std::rand() % 3000;
boost::posix_time::time_duration randomDelay =
boost::posix_time::milliseconds(randomMilliseconds);
boost::this_thread::sleep(randomDelay);
// Post some random value
float value = std::rand() % 100;
{
LockType lock(coutMutex);
std::cout << "Thread " << boost::this_thread::get_id() << " posting "
<< value << " after " << randomMilliseconds << "ms\n";
}
accumulator.accumulateAndWait(value);
float avg = accumulator.average();
// Print a message to indicate this thread is past the barrier.
{
LockType lock(coutMutex);
std::cout << "Thread " << boost::this_thread::get_id() << " unblocked. "
<< "Average = " << avg << "\n" << std::flush;
}
}
int main()
{
int workerThreadCount = 5;
Accumulator accumulator(workerThreadCount);
// Create and launch worker threads
boost::thread_group threadGroup;
for (int i=0; i<workerThreadCount; ++i)
{
threadGroup.create_thread(
boost::bind(&workerFunction, boost::ref(accumulator)));
}
// Wait for all worker threads to finish
threadGroup.join_all();
{
LockType lock(coutMutex);
std::cout << "All worker threads finished\n" << std::flush;
}
/* Pause a bit before exiting, to give worker threads a chance to
print their messages. */
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
I get the following output:
Thread 0x100100f80 posting 72 after 1073ms
Thread 0x100100d30 posting 44 after 1249ms
Thread 0x1001011d0 posting 78 after 1658ms
Thread 0x100100ae0 posting 23 after 1807ms
Thread 0x100101420 posting 9 after 1930ms
Thread 0x100101420 unblocked. Average = 45.2
Thread 0x100100f80 unblocked. Average = 45.2
Thread 0x100100d30 unblocked. Average = 45.2
Thread 0x1001011d0 unblocked. Average = 45.2
Thread 0x100100ae0 unblocked. Average = 45.2
All worker threads finished