-1

I am trying to implement the Producer-Consumer problem operating system using semaphore and pthread. But my output is totally different from expected. Here is my code:

    #include<iostream>
    #include<pthread.h>
    #include<fstream>
    #include<unistd.h>
    #include<queue>

    // define queue size
    #define QUEUE_SIZE 5

    // declare and initialize semaphore and read/write counter
    static int semaphore = 1;
    static int counter = 0;

    // Queue for saving characters
    static std::queue<char> charQueue;

    // indicator for end of file
    static bool endOfFile = false;

    // save arrays
    char consumerArray1[100];
    char consumerArray2[100];

    // function to wait for semaphore
    void wait()
    {
        while(semaphore<=0);
        semaphore--;
    }

    // function to signal the wait function
    void signal()
    {
        semaphore++;
    }

    void *Producer(void *ptr)
    {
        int i=0;
        std::ifstream input("string.txt");
        char temp;
        while(input>>temp)
        {
            wait();
            charQueue.push(temp);
            //std::cout<<"Producer:\nCounter: "<<counter<<" Semaphore: "<<semaphore<<std::endl;
            counter++;
            std::cout<<"Procuder Index: "<<i<<std::endl;
            i++;
            signal();
            sleep(2);
        }
        endOfFile = true;
        pthread_exit(NULL);
    }

    void *Consumer1(void *ptr)
    {
        std::cout<<"Entered consumer 1:"<<std::endl;
        int i = 0;
        while(counter<=0);
        while(!endOfFile)
        {
            while(counter<=0);
            wait();
            //std::cout<<"Consumer1:\nCounter: "<<counter<<" Semaphore: "<<semaphore<<std::endl;
            consumerArray1[i] = charQueue.front();
            charQueue.pop();
            i++;
            counter--;
            std::cout<<"Consumer1 index:"<<i<<" char: "<<consumerArray1[i]<<std::endl;
            signal();
            sleep(2);
        }
        consumerArray1[i] = '\0';
        pthread_exit(NULL);
    }

    void *Consumer2(void *ptr)
    {
        std::cout<<"Entered consumer 2:"<<std::endl;
        int i = 0;
        while(counter<=0);
        while(!endOfFile)
        {
            while(counter<=0);
            wait();
            //std::cout<<"Consumer2:\nCounter: "<<counter<<" Semaphore: "<<semaphore<<std::endl;
            consumerArray2[i] = charQueue.front();
            charQueue.pop();
            i++;
            counter--;
            std::cout<<"Consumer2 index: "<<i<<" char: "<<consumerArray2[i]<<std::endl;
            signal();
            sleep(4);
        }
        consumerArray2[i] = '\0';
        pthread_exit(NULL);
    }

    int main()
    {
        pthread_t thread[3];
        pthread_create(&thread[0],NULL,Producer,NULL);
        int rc = pthread_create(&thread[1],NULL,Consumer1,NULL);
        if(rc)
        {
            std::cout<<"Thread not created"<<std::endl;
        }
        pthread_create(&thread[2],NULL,Consumer2,NULL);
        pthread_join(thread[0],NULL);pthread_join(thread[1],NULL);pthread_join(thread[2],NULL);
        std::cout<<"First array: "<<consumerArray1<<std::endl;
        std::cout<<"Second array: "<<consumerArray2<<std::endl;
        pthread_exit(NULL);
    }

The problem is my code, in some runs freezes(probably in an infinite loop) after the entire file has been read. And also both of the consumer functions read the same words even though I am popping it out after reading. Also the part of printing the array element that has been read just prints blank. Why are these problems happening? I am new to threads(as in coding using threads, I know theoretical concepts of threads) so please help me with this problem.

Shantanu Shinde
  • 639
  • 11
  • 33
  • 2
    Either a proper interlocked modification mechanism is required for what you're doing, or some mutual exclusion mechanics. Having either can work. Having neither is a recipe for race conditions, hung code, etc. You need to learn things like mutexes, critical sections, etc. unrelated, but minor compared to the threading woes, [this is wrong: `while(!input.eof())`](https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). There is also no input validation whatsoever. – WhozCraig Apr 17 '19 at 02:33
  • @WhozCraig I have added mutual exclusion using the `wait` and `signal` functions. what is wrong with that? – Shantanu Shinde Apr 17 '19 at 03:31
  • what's the reason for downvote? what is wrong with the question? – Shantanu Shinde Apr 17 '19 at 03:52
  • 1
    @ShantanuShinde It's odd for someone who is new to threads to start out by trying to write their own synchronization primitives. That's an extremely complicated thing to get right and should not be attempted until you're very, very familiar with how they're used. – David Schwartz Apr 17 '19 at 04:01
  • @DavidSchwartz well, I have been given an assignment to do so. – Shantanu Shinde Apr 17 '19 at 04:12
  • @ShantanuShinde That's unfortunate because it's extremely complicated to get right. There are a long list of remarkably subtle things that require deep CPU knowledge to get right. For example, your `while(semaphore<=0); semaphore--;` causes the CPU's branch prediction to think the `while` loop will continue again when you finally do get the semaphore so you take a mispredicted branch and pipeline stall right at the instant it's most critical that you operate as quickly as possible. Without knowing that, you might have thought your code was reasonable. – David Schwartz Apr 17 '19 at 04:35
  • Also, what if the CPU has hyper-threading and one virtual core is doing `while(sempahore<=0);` while the other virtual core is running the code that holds the semaphore? How is the thread that holds the sempahore supposed to make efficient forward progress when the other thread running in that same virtual core is spinning in a tight loop consuming the resources they share? And what effect will that have on the CPU's power/thermal budget that the thread doing the actual work needs? – David Schwartz Apr 17 '19 at 04:37
  • @DavidSchwartz Well as you told in the answer to use semaphore.h library, I am trying to use that. One question is what constrain should I put on the consumers so they keep on reading the queue, but not read if the queue is empty. the counter loop is not working, it goes into infinite loop. – Shantanu Shinde Apr 17 '19 at 05:02
  • @ShantanuShinde You would probably do well to learn the (unfortunately, somewhat complex) way that condition variables and mutexes work together. It is the "pthreads way" to use this method. Have you seen any pthreads code that uses mutexes and condition variables? They are among the very first synchronization mechanisms you should learn. – David Schwartz Apr 17 '19 at 05:52

1 Answers1

2

The pthreads standard prohibits accessing an object in one thread while another thread is, or might be, modifying it. Your wait and signal functions violate this rule by modifying semaphore (in signal) while a thread calling wait might be accessing it. You do this with counter as well.

If what you were doing in signal and wait were legal, you wouldn't need signal and wait. You could just access the queue directly the same way you access semaphore directly. If the queue needs protection (as I hope you know it does) then semaphore needs protection too and for exactly the same reason.

The compiler is permitted to optimize this code:

while(semaphore<=0);

To this code:

if (semaphore<=0) { while (1); }

Why? Because it knows that no other thread can possibly modify semaphore while this thread could be accessing it since that is prohibited by the standard. Therefore, there is no reason to read more than once.

You need to use actual sempahores and/or locks.

David Schwartz
  • 166,415
  • 16
  • 184
  • 259
  • how do I use actual semaphore? – Shantanu Shinde Apr 17 '19 at 03:50
  • Either punch "pthread semaphore" into your favorite search engine or read [this](http://www.csc.villanova.edu/~mdamian/threads/posixsem.html) or [this](https://www.geeksforgeeks.org/use-posix-semaphores-c/). – David Schwartz Apr 17 '19 at 03:59
  • I changed the code to use the semaphore header but I am facing a different problem now. Where should I post the new code? – Shantanu Shinde Apr 17 '19 at 05:28
  • You can ask a new question. Make sure there is no way any object (except a synchronization object explicitly designed for this purpose) can be accessed in one thread while another thread is, or might be, modifying it. – David Schwartz Apr 17 '19 at 05:51
  • what is the downvote for though? I have posted the question according to the guidelines of stackoverflow. – Shantanu Shinde Apr 17 '19 at 07:21
  • please look into that problem. I made a new question: https://stackoverflow.com/questions/55722150/need-help-in-implementing-the-producer-consumer-problem-with-pthread-and-semapho – Shantanu Shinde Apr 17 '19 at 07:29