0

Here I have created 2 producer threads and 2 consumer threads. They put and take out values only in and from one shared queue.

Problem is that first producer does fill in and then gets into waiting mode.

After that no other thread runs. Explain please what point am I missing.

#include "mainwindow.h"
#include <QApplication>

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <iostream>
#include <QDebug>


pthread_mutex_t mutexVariable     = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  conditionVariable = PTHREAD_COND_INITIALIZER;

int numberOfActiveProducers;
int numberOfActiveConsumers;

QList <int> sharedQueue;
/*
 * `sharedQueue`'s size is assumed to be 10 ATM.
 * `sharedQueue` is supposed to be shared among two threads.
 * Producer threads will put the 1's in it, and Consumer threads will remove the 1's.
 * Assumption: `sharedQueue` can contain only 10 elements at a time.
 */

int sizeOfSharedQueue;

//  This function is run by the thread `Producer A`.
void *threadProducerAFunction (void *arg) {
    Q_UNUSED (arg);

    while (1) {
        qDebug () << "\nProducer A";

        pthread_mutex_lock (&mutexVariable);

        if (sharedQueue.length () < 10) {
            sharedQueue.push_back (1);
            qDebug () << "\nPushed by Producer A: Length of queue is: " << sharedQueue.length ();
        }
        else {
            qDebug () << "\nProducer A has done its bit and is now in waiting mode. Length of queue is: " << sharedQueue.length ();
            pthread_cond_wait (&conditionVariable, &mutexVariable);
        }

        pthread_mutex_unlock (&mutexVariable);
    }

    return NULL;
}

//  This function is run by the thread `ProducerB`.
void *threadProducerBFunction (void *arg) {
    Q_UNUSED (arg);

    while (1) {
        qDebug () << "\nProducer B";

        pthread_mutex_lock (&mutexVariable);

        if (sharedQueue.length () < 10) {
            sharedQueue.push_back (1);
            qDebug () << "\nPushed by Producer B: Length of queue is: " << sharedQueue.length ();
        }
        else {
            qDebug () << "\nProducer B has done its bit and is now in waiting mode. Length of queue is: " << sharedQueue.length ();
            pthread_cond_wait (&conditionVariable, &mutexVariable);
        }

        pthread_mutex_unlock (&mutexVariable);
    }

    return NULL;
}

//  This function is run by the thread `Consumer A`.
void *threadConsumerAFunction (void *arg) {
    Q_UNUSED (arg);

    while (1) {
        qDebug () << "\nConsumer A";

        pthread_mutex_lock (&mutexVariable);

        if (sharedQueue.length () > 0) {
            sharedQueue.pop_front ();
            qDebug () << "\nRemoved by thread Consumer A. Length of queue is: " << sharedQueue.length ();
        }
        else {
            pthread_cond_signal (&conditionVariable);
            qDebug () << "\nSignal issued by thread Consumer A. Length of queue is: " << sharedQueue.length ();
        }

        pthread_mutex_unlock (&mutexVariable);
    }   
    return NULL;
}

//  This function is run by the thread `Consumer B`.
void *threadConsumerBFunction (void *arg) {
    Q_UNUSED (arg);

    while (1) {
        qDebug () << "\nConsumer B";

        pthread_mutex_lock (&mutexVariable);

        if (sharedQueue.length () > 0) {
            sharedQueue.pop_front ();
            qDebug () << "\nRemoved by thread Consumer B. Length of queue is: " << sharedQueue.length ();
        }
        else {
            pthread_cond_signal (&conditionVariable);
            qDebug () << "\nSignal issued by thread Consumer B. Length of queue is: " << sharedQueue.length ();
        }

        pthread_mutex_unlock (&mutexVariable);
    }
    return NULL;
}

int main (int argc, char *argv[]) {
    numberOfActiveProducers = 2;
    numberOfActiveConsumers = 2;
    sizeOfSharedQueue       = 10;

    // `sharedQueue` initialization by 0.
    for (int i = 0; i < sizeOfSharedQueue; i++) {
        sharedQueue.push_back (0);
    }

    // Producer threads creation and joining
    pthread_t producerA;
    pthread_t producerB;

    if (pthread_create (&producerA, NULL, threadProducerAFunction, NULL)) {
        fprintf (stderr, "Error creating thread Producer A\n");
        return 1;
    }

    if (pthread_join (producerA, NULL)) {
        fprintf (stderr, "Error joining thread Producer A\n");
        return 2;
    }

    if (pthread_create (&producerB, NULL, threadProducerBFunction, NULL)) {
        fprintf (stderr, "Error creating thread Producer A\n");
        return 1;
    }

    if (pthread_join (producerB, NULL)) {
        fprintf (stderr, "Error joining thread Producer B\n");
        return 2;
    }

    // Consumer threads creation and joining
    pthread_t consumerA;
    pthread_t consumerB;

    if (pthread_create (&consumerA, NULL, threadConsumerAFunction, NULL)) {
        fprintf (stderr, "Error creating thread Consumer A\n");
        return 1;
    }

    if (pthread_join (consumerA, NULL)) {
        fprintf (stderr, "Error joining thread Consumer A\n");
        return 2;
    }

    if (pthread_create (&consumerB, NULL, threadConsumerBFunction, NULL)) {
        fprintf (stderr, "Error creating thread Consumer B\n");
        return 1;
    }

    if (pthread_join (consumerB, NULL)) {
        fprintf (stderr, "Error joining thread Consumer B\n");
        return 2;
    }

    QApplication a (argc, argv);
    MainWindow w;
    w.show ();

    return a.exec ();
}
Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411
  • Please share with us the debug output that you get. – kaylum Dec 18 '15 at 01:23
  • Oh, you have a `pthread_join` after creating each thread in `main`. That won't work as the `pthread_join` will not return until the thread it is waiting for exits. And none of your threads exit. So of course no other threads even get created after the first producer thread is created. – kaylum Dec 18 '15 at 01:25
  • @kaylum what am i supposed to correct? Am I not suppsoed to write joins like that? – Aquarius_Girl Dec 18 '15 at 01:26
  • 1
    Put all the join calls after all the create calls. Or leave them out altogether. You only need them if you really care about the results from the threads after they exit (you don't as they don't exit) or you want to prevent the main thread from exiting prematurely (which I think you are already ensuring with the `a.exec()`). – kaylum Dec 18 '15 at 01:26
  • @kaylum please write a proper answer with all details. – Aquarius_Girl Dec 18 '15 at 01:34
  • @TheIndependentAquarius "all details"? *'Prak was instructed to tell "the Truth, the Whole Truth, and Nothing but the Truth,' which he did, in its entirety."* (Douglas Adams, cf. http://hitchhikers.wikia.com/wiki/Prak) That incident was quite scary. Be careful what you ask for. – Peter - Reinstate Monica Dec 18 '15 at 01:53

2 Answers2

2

The problem is that there are pthread_join calls after each pthread_create call in main. pthread_join by definition will block until the thread it is waiting for exits. Since none of the child threads exit the result is that the first pthread_join call will block indefinetely and hence none of the subsequent pthread_create calls are executed.

One fix is to just remove all the pthread_join calls. pthread_join is typically used to wait for and get the return status of child threads or to synchronise the main thread so that it does not exit before the child thread has completed. So those pthread_join calls are are not actually needed in this case because the child threads do not exit and the main thread calls a.exec() which performs the task of preventing it from exiting.

Unrelated to the actual question but I see that you have essentially duplicated the producer and consumer code for each thread. That is unnecessary as the same thread function can be passed to multiple pthread_create calls (as long as there are no static variables). If you want to differentiate the instances for debugging purposes either use the thread id or pass in a different arg to each thread for identification.

kaylum
  • 13,833
  • 2
  • 22
  • 31
  • when do we need separate functions for each thread then? Examples please. – Aquarius_Girl Dec 18 '15 at 01:44
  • @TheIndependentAquarius When they are to do different things ;-). Take a web server of a big company which spawns a thread for each connection. There are not 600 `serveConnection[AA-ZZ]()` functions coded separately... Each execution thread has its own set of local variables in the function, similar to a recursive call of the same function, and of course its own program counter. – Peter - Reinstate Monica Dec 18 '15 at 01:47
  • @TheIndependentAquarius I can't think of a practical (non-contrived) example of where you would need seperate functions where the threads are doing essentially the same work. There is almost never a good reason to have seperate functions in that case. The `arg` parameter essentially serves that purpose - allows state to be passed in to the thread to parameterize its behaviour if required. – kaylum Dec 18 '15 at 01:48
  • @PeterA.Schneider thanks for some explanation. Kaylum thanks to you too. So, what should be the argument to the function which will be same for all? the thread id? Will you show a little demonstration program with one function being accessed by all the producers? – Aquarius_Girl Dec 18 '15 at 06:39
1

You are mixing frameworks.

If you're already using Qt, it comes with a whole range of threading classes that make life a lot easier.

I've transformed your code into the Qt equivalents and it works properly.

#include <QtCore/qthread.h>
#include <QtWidgets/QApplication>
#include <QtCore/qmutex.h>
#include <QtCore/qwaitcondition.h>
#include <QtCore/QList.h>
#include <QtCore/qdebug.h>

QMutex mutex;
QWaitCondition waitCondition;

int numberOfActiveProducers;
int numberOfActiveConsumers;

QList<int> sharedQueue;

class Producer : public QThread{
public:
    Producer(QString const &label) : label(label) {}

    void run() {
        forever {
            qDebug() << QString("\nProducer %1").arg(label);

            QMutexLocker locker(&mutex);

            if (sharedQueue.length() < 10){
                sharedQueue << 1;
                qDebug() << QString("\nPushed by Producer %1: Length of queue is: %2").arg(label).arg(sharedQueue.length());
            } else {
                qDebug() << QString("\nProducer %1 has done its job and is now in waiting mode. Length of queue is: %2").arg(label).arg(sharedQueue.length());
                waitCondition.wait(&mutex);
            }
        }
    }

private:
    QString label;
};

class Consumer : public QThread{
public:
    Consumer(QString const &label) : label(label) {}

    void run(){
        forever {
            qDebug() << QString("\nConsumer %1").arg(label);

            QMutexLocker locker(&mutex);

            if (sharedQueue.length() > 0){
                sharedQueue.takeFirst();
                qDebug() << QString("\nRemoved by thread Consumer %1. Length of queue is: %2").arg(label).arg(sharedQueue.length());
            } else {
                waitCondition.wakeAll();
                qDebug() << QString("\nSignal issued by thread Consumer %1. Length of queue is: %2").arg(label).arg(sharedQueue.length());
            }
        }
    }

private:
    QString label;
};

int main(int argc, char **argv){
    numberOfActiveConsumers = 2;
    numberOfActiveProducers = 2;

    QCoreApplication a(argc, argv);

    Producer producerA("A");
    Producer producerB("B");
    Consumer consumerA("A");
    Consumer consumerB("B");

    producerA.start();
    producerB.start();
    consumerA.start();
    consumerB.start();

    return a.exec();
}

I must just add though that, I would not normally use QThread subclasses for just a simple function unless I'd need access to the thread directly. Normally, I'd subclass QRunnable and give objects to QThreadPool to start.

RobbieE
  • 4,280
  • 3
  • 22
  • 36